Caplane

History

Every lien ever recorded for one borrower, swept from the registry's own events.

The only question a caller can ask without having first been handed a lien id: how much a borrower has already pledged, and until when. It does not answer whether one specific receivable is pledged — that needs a lien id from the Lien reference.

Reference

liensOf

function liensOf(
  client: CaplaneClient,
  borrower: `0x${string}`,
  options?: { fromBlock?: bigint },
): Promise<RecordedLien[]>

Sweeps LienRecorded events for one borrower, paginated by MAX_SPAN blocks per request and retried with withRetry. Runs against a single endpoint (client.read), not the quorum every other read in this package uses — the two default Arc Testnet endpoints do not share a log-range limit, so comparing them would need on the order of a hundred paginated requests per endpoint against a rate limiter. A caller who minds an endpoint quietly omitting an event should pass their own node as client.read.

import { createCaplaneClient, liensOf } from 'caplane-sdk'

const client = createCaplaneClient()
const found = await liensOf(client, borrower)

Used in production by the MCP server's liens_of_borrower tool (services/mcp/src/tools.ts). The public registry lookup's borrower search (web/app/[mode]/registry/history.ts) reimplements the same sweep by hand against raw eth_getLogs, for the same bundle-size reason isEncumbered does — it mirrors this function's pagination and retry shape rather than importing it.

DEPLOYED_AT

const DEPLOYED_AT: bigint // 61_681_981n

The block the registry was deployed at, and the default fromBlock for liensOf, submissionsOf, and outcomeOf. A head-relative default would read as an empty history for anything older than MAX_SPAN blocks — a lender misreading that as nothing pledged.

MAX_SPAN

const MAX_SPAN: bigint // 10_000n

The widest block range a single eth_getLogs call is allowed — the default Arc Testnet endpoint refuses a wider one with -32614 eth_getLogs is limited to a 10,000 range. Every sweep in this package pages by this span.

withRetry

function withRetry<T>(ask: () => Promise<T>, baseDelayMs?: number): Promise<T>

Retries a rejected page instead of losing the sweep — the initial call plus up to five retries, doubling from baseDelayMs (default 2_000). Covers the endpoint shedding load under a legal query (-32012 requested range too large) and plain rate limiting (-32005 rate limit exceeded); a rejection that never clears is still raised after the sixth call (the initial attempt and all five retries). Every paginated read in this package (liensOf, submissionsOf, outcomeOf) calls this internally — reach for it directly only when sweeping getLogs yourself against the same endpoints.

RecordedLien

type RecordedLien = {
  lienId: `0x${string}`
  borrower: `0x${string}`
  expiresAt: bigint
  blockNumber: bigint
}

One element of what liensOf resolves to — the fields available from a LienRecorded event alone, without a follow-up call to lienOf.