# PoolSessionBuilder

> Entry point for the TypeScript SDK. Wires config to a runnable PoolSession.

`PoolSessionBuilder` wires SDK configuration to a runnable [`PoolSession`](/sdk/pool-session). There are two paths: a fluent builder with optional `.withX()` overrides, and a one-call `create()` shortcut for the simple case.

```ts
static create(config: PoolSessionSimpleConfig): Promise<PoolSession>
static fromConfig<Cfg extends PoolSessionBuilderConfig>(config: Cfg): PoolSessionBuilder<Cfg>
create(): Promise<PoolSession>   // instance terminator
```

A builder is single-use: after a successful `create()` it is consumed, and reusing it throws `InvalidBuilderConfig`. Validation happens in the instance `create()`, not in `fromConfig`.

## Two construction paths

### Simple: one call

```ts
const session = await PoolSessionBuilder.create({
    chainId: 11155111,
    rpcUrl,
    ownerAddress,
    protocolKeys,
    aspUrl: "https://api-dev.0xbow.io",
    relayers,
    circuitArtifactsDir: "/path/to/circuits",   // Node only
});
```

### Fluent: for custom interactors

```ts

const session = await PoolSessionBuilder
    .fromConfig({
        chainId,
        rpcUrl,
        ownerAddress,
        protocolKeys,
        aspUrl,
        relayers,
        walletInteractor: { type: "viem", walletClient },
    })
    .withCircuitArtifacts(new FetchCircuitArtifacts("/circuits"))  // browser
    .create();
```

[`FetchCircuitArtifacts`](/sdk/circuit-artifacts) is a sample-app helper, not an SDK export. Copy `v2-monorepo/apps/sample-web/src/FetchCircuitArtifacts.ts` or provide your own `ICircuitArtifacts` implementation.

## Config fields

| Field | Type | Required | Notes |
|---|---|---|---|
| `chainId` | `number` | yes | Sepolia is `11155111` |
| `rpcUrl` | `string` | yes | HTTPS JSON-RPC endpoint |
| `ownerAddress` | `Address` | yes | the address that derived `protocolKeys` |
| `protocolKeys` | `ProtocolKeys` | yes* | not needed with `.withKeystoreManager(...)` |
| `aspUrl` | `string` | yes* | or `ipfsGatewayUrl`, or `.withAspDataProvider(...)` |
| `relayers` | `RelayerInfo[]` | yes* | at least one entry, unless `.withRelayerInteractor(...)` |
| `deployment` | `DeploymentAddresses` | no | defaults to `DEPLOYMENTS[chainId]` |
| `walletInteractor` | `WalletInteractorConfig` | no | `{type: "viem", walletClient}` or `{type: "EIP1193", provider}`; required for execute paths, omit for read-only sessions |
| `persistentStorage` | `PersistentStorageConfig` | no | `{type: "local", prefix?}` (browser), `{type: "file", dirPath, filename?}` (Node), `{type: "memory"}`; without it, note state lives in memory only |
| `circuitArtifactsDir` | `string` | no | local artifacts directory (Node); mutually exclusive with the IPFS path |
| `circuitGatewayUrls` | `string[]` | no | IPFS gateways for artifacts; requires `circuitManifest` |
| `circuitManifest` | `CircuitManifest` | no* | required on the IPFS artifacts path, with pinned CIDs; there is no silent default |
| `ipfsGatewayUrl` | `string` | no | switches the ASP data provider to IPFS |
| `aspPublicKey` | `PublicKey` | no | fetched from the provider on first use when omitted |
| `httpClientConfig` | `HTTPClientConfig` | no | timeouts and fetch options for the shared HTTP client |
| `logger` | `ILogger` | no | shared across services |

## Fluent overrides

Every override returns the builder. Overrides that replace something the config also describes are conflict-guarded: supplying both the config field and the override fails `create()` with an `InvalidBuilderConfig` telling you to remove one.

| Override | Replaces | Conflicts with config field |
|---|---|---|
| `withKeystoreManager(impl)` | key management | `protocolKeys` |
| `withRelayerInteractor(impl)` | relayer client | `relayers` |
| `withCircuitArtifacts(impl)` | artifact loading | `circuitArtifactsDir`, `circuitGatewayUrls` |
| `withCircuitCacheStore(store)` | artifact caching | local artifacts path |
| `withAspDataProvider(impl)` | ASP data source | `aspUrl`, `ipfsGatewayUrl` |
| `withAspClient(impl)` | ASP client | `aspPublicKey` |
| `withEntrypointInteractor(impl)`, `withKeystoreInteractor(impl)`, `withVaultInteractor(impl)`, `withAspRegistryInteractor(impl)` | per-contract interactors | `deployment` |
| `withStorageService(impl)`, `withNoteStorageAdapter(impl)`, `withPaymentRequestStorageAdapter(impl)` | persistence | `persistentStorage` (and each other, partially) |
| `withWalletInteractor(impl)` | wallet | `walletInteractor` |
| `withCryptoService`, `withHashService`, `withMerkleService`, `withHttpClient`, `withRpcInteractor`, `withGroth16Prover`, `withProofService`, `withWitnessPreparationService`, `withNoteComputationService`, `withNoteManager`, `withNoteDiscoveryService`, `withPaymentRequestService` | individual services | none |

## Errors

`create()` throws `InvalidBuilderConfig` for every configuration failure except one: when `deployment` is omitted and `chainId` has no entry in `DEPLOYMENTS`, it throws `UnsupportedChainId`. Both classes are runtime exports of the package, so `instanceof` works for these two. The conditions behind `InvalidBuilderConfig`:

-   schema validation failure (the message carries the first Zod issue as `path: message`; log the full `issues` array when debugging, see [Debugging](../dev-guide/debugging))
-   a config field conflicting with a fluent override (the message names both)
-   reusing a consumed builder
-   missing `aspUrl`/`ipfsGatewayUrl` without an ASP override; missing `protocolKeys` without a keystore-manager override; an empty or missing `relayers` array without a relayer override
-   the IPFS artifacts path without an explicit `circuitManifest`, or a manifest with no pinned CIDs
-   a wallet that returns no accounts, or whose account or chain doesn't match `ownerAddress` / `chainId`

## Versions and compatibility

`protocolKeys` requires a `revocableKeyIndex` field (`"0x0"` for fresh accounts). The SDK, circuits, and deployed verifiers must all belong to the same deployment set (see [Sepolia V9 addresses](../deployments/sepolia#contract-addresses-sepolia-v9)).
