# Synapse Core

import { Tabs, TabItem } from '@astrojs/starlight/components';

**Synapse Core** (`@filoz/synapse-core`) is the low-level foundation that both the [Synapse SDK](/developer-guides/synapse/) and [Synapse React](/developer-guides/synapse-react/) build on. Use Synapse Core when you need:

- Direct control over individual contract calls without the `Synapse` class abstraction
- Custom integrations that combine only the modules you need

## Setup

**Install dependencies:**

<Tabs syncKey="pkg">
  <TabItem label="npm" icon="seti:npm">
    ```bash
    npm install @filoz/synapse-core viem
    ```
  </TabItem>
  <TabItem label="pnpm" icon="pnpm">
    ```bash
    pnpm add @filoz/synapse-core viem
    ```
  </TabItem>
  <TabItem label="yarn" icon="seti:yarn">
    ```bash
    yarn add @filoz/synapse-core viem
    ```
  </TabItem>
  <TabItem label="bun" icon="bun">
    ```bash
    bun add @filoz/synapse-core viem
    ```
  </TabItem>
</Tabs>

Synapse Core requires [viem](https://viem.sh) 2.x as a peer dependency.

**Client setup:**

All Synapse Core functions accept a viem `Client` as their first argument. Create one using viem's standard client factories:

```ts twoslash
// @lib: esnext,dom
import { createPublicClient, createWalletClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { calibration, mainnet } from "@filoz/synapse-core/chains"

// Read-only client for queries
const publicClient = createPublicClient({
  chain: calibration, // or mainnet
  transport: http(),
})

// Wallet client for transactions
const account = privateKeyToAccount("0x...")
const walletClient = createWalletClient({
  account,
  chain: calibration, // or mainnet
  transport: http(),
})
```

The `@filoz/synapse-core/chains` subpath exports chain definitions with all contract addresses pre-configured for Filecoin Mainnet (`mainnet`) and Filecoin testnet (`calibration`) networks.

## Payments

Query account balances, deposit funds, manage operator approvals, and settle payment rails on the Filecoin Pay contract.

```ts twoslash
// @lib: esnext,dom
import { createPublicClient, createWalletClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { calibration } from "@filoz/synapse-core/chains"
const publicClient = createPublicClient({ chain: calibration, transport: http() })
const account = privateKeyToAccount("0x...")
const walletClient = createWalletClient({ account, chain: calibration, transport: http() })
// ---cut---
import * as pay from "@filoz/synapse-core/pay"
import { parseUnits } from "viem"

// Query Filecoin Pay account info for the USDFC token
const info = await pay.accounts(publicClient, {
  // If not provided, the USDFC token address will be used.
  token: calibration.contracts.usdfc.address,
  address: account.address,
})
console.log(info.availableFunds) // bigint — funds available in Filecoin Pay

// Deposit USDFC in Filecoin Pay
const depositTxHash = await pay.depositAndApprove(walletClient, {
  // If not provided, the USDFC token address will be used.
  token: calibration.contracts.usdfc.address,
  amount: parseUnits("1", 18), // 1 USDFC
})

await publicClient.waitForTransactionReceipt({
  hash: depositTxHash
})

// Withdraw USDFC from Filecoin Pay
const withdrawTxHash = await pay.withdraw(walletClient, {
  // If not provided, the USDFC token address will be used.
  token: calibration.contracts.usdfc.address,
  amount: parseUnits("1", 18), // 1 USDFC
})

await publicClient.waitForTransactionReceipt({
  hash: withdrawTxHash
})

```

## Storage

List approved storage providers.

```ts twoslash
// @lib: esnext,dom
import { createPublicClient, http } from "viem"
import { calibration } from "@filoz/synapse-core/chains"
const publicClient = createPublicClient({ chain: calibration, transport: http() })
// ---cut---
import * as warmStorage from "@filoz/synapse-core/warm-storage"

// List approved providers
const providers = await warmStorage.getApprovedProviderIds(publicClient)
console.log(providers) // bigint[] — approved provider IDs
```

---

Create a data set and upload a file.

```ts twoslash
// @lib: esnext,dom
import { createWalletClient, createPublicClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { calibration } from "@filoz/synapse-core/chains"
const account = privateKeyToAccount("0x...")
const walletClient = createWalletClient({ account, chain: calibration, transport: http() })
const publicClient = createPublicClient({ chain: calibration, transport: http() })
const data = new Uint8Array([1, 2, 3])
// ---cut---
import * as piece from "@filoz/synapse-core/piece"
import * as sp from "@filoz/synapse-core/sp"
import { getPDPProvider } from "@filoz/synapse-core/sp-registry"

// 1. Select a provider
const provider = await getPDPProvider(publicClient, { providerId: 1n })

// 2. Calculate PieceCID and upload data
const pieceCid = piece.calculate(data)
const size = piece.getSize(pieceCid)
console.log(size) // size of the piece in bytes
// Upload the piece to the provider
await sp.uploadPiece({
  data,
  pieceCid,
  serviceURL: provider.pdp.serviceURL,
})
// Poll the provider to confirm the piece is stored
await sp.findPiece({
  pieceCid,
  serviceURL: provider.pdp.serviceURL,
  retry: true,
})

console.log(`Piece ${pieceCid.toString()} uploaded to provider ${provider.pdp.serviceURL}`)

// 3. Create a data set and add the piece on-chain
const result = await sp.createDataSetAndAddPieces(walletClient, {
  serviceURL: provider.pdp.serviceURL,
  payee: provider.payee,
  cdn: false,
  pieces: [{ pieceCid }],
})

// 4. Wait for confirmation
const confirmed = await sp.waitForCreateDataSetAddPieces({
  statusUrl: result.statusUrl,
})

const { dataSetId, piecesIds, hash } = confirmed

console.log(`Data set ${dataSetId} created and piece ${pieceCid.toString()} added to data set`)
```

<details>
<summary>*For large files, use `uploadPieceStreaming` instead of `uploadPiece`:*</summary>

```ts twoslash
// @lib: esnext,dom
import * as sp from "@filoz/synapse-core/sp"
const serviceURL = "https://provider.example.com"
// ---cut---
// Stream upload — PieceCID is calculated during transfer
const stream = new ReadableStream<Uint8Array>({ /* ... */ })
const uploaded = await sp.uploadPieceStreaming({
  data: stream,
  serviceURL,
  onProgress(bytes) {
    console.log(`${bytes} bytes uploaded`)
  },
})
console.log(uploaded.pieceCid) // PieceCID — calculated from streamed data
console.log(uploaded.size)     // number — total bytes uploaded
```

</details>

---

Download a piece from a storage provider.

```ts twoslash
// @lib: esnext,dom
import { createPublicClient, http } from "viem"
import { getPDPProvider } from "@filoz/synapse-core/sp-registry"
import { calibration } from "@filoz/synapse-core/chains"
const publicClient = createPublicClient({ chain: calibration, transport: http() })
const provider = await getPDPProvider(publicClient, { providerId: 1n })
// ---cut---
import { downloadAndValidate } from "@filoz/synapse-core/piece"

// Download a piece
const data = await downloadAndValidate({
  url: `${provider.pdp.serviceURL}/piece`,
  expectedPieceCid: "bafkzcib...",
})
console.log(data) // Uint8Array — downloaded piece data
```

### Provider Selection

For custom upload pipelines, you can use the provider selection functions directly to find matching data sets and providers:

```ts twoslash
// @lib: esnext,dom
import { createWalletClient, createPublicClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { calibration } from "@filoz/synapse-core/chains"
const account = privateKeyToAccount("0x...")
const walletClient = createWalletClient({ account, chain: calibration, transport: http() })
const publicClient = createPublicClient({ chain: calibration, transport: http() })
// ---cut---
import { fetchProviderSelectionInput, selectProviders } from "@filoz/synapse-core/warm-storage"

// Fetch all chain data needed for selection
const input = await fetchProviderSelectionInput(publicClient, {
  address: account.address,
})

// Primary: pass endorsedIds to restrict pool to endorsed providers only
const [primary] = selectProviders({
  ...input,
  count: 1,
  metadata: { source: "my-app" },
})

// Secondary: pass empty set to allow any approved provider
const [secondary] = selectProviders({
  ...input,
  endorsedIds: [],
  count: 1,
  excludeProviderIds: [primary.provider.id],
  metadata: { source: "my-app" },
})
```

`fetchProviderSelectionInput()` makes a single multicall to gather providers, endorsements, and existing data sets. `selectProviders()` is a pure function (no network calls) that applies a 2-tier preference within the eligible pool:

1. Existing data set with matching metadata
2. New data set (no matching data set found)

The `endorsedIds` parameter controls which providers are eligible. When non-empty, **only** endorsed providers can be selected (no fallback to non-endorsed). When empty, all approved providers are eligible.

### SP-to-SP Pull

Transfer pieces between providers without using client bandwidth:

```ts twoslash
// @lib: esnext,dom
import { createWalletClient, createPublicClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { calibration } from "@filoz/synapse-core/chains"
const account = privateKeyToAccount("0x...")
const walletClient = createWalletClient({ account, chain: calibration, transport: http() })
const publicClient = createPublicClient({ chain: calibration, transport: http() })
import { getPDPProvider } from "@filoz/synapse-core/sp-registry"
const primaryProvider = await getPDPProvider(publicClient, { providerId: 1n })
const secondaryProvider = await getPDPProvider(publicClient, { providerId: 2n })
const pieceCid = "bafkzcib..." as any;
// ---cut---
import * as SP from "@filoz/synapse-core/sp"

const response = await SP.waitForPullPieces(walletClient, {
  serviceURL: secondaryProvider.pdp.serviceURL,
  pieces: [{
    pieceCid,
    sourceUrl: `${primaryProvider.pdp.serviceURL}/pdp/piece/${pieceCid}`,
  }],
  payee: secondaryProvider.payee,
  payer: account.address,
  cdn: false,
  metadata: {},
})
```

This path requires manual EIP-712 signing. The `signAddPieces` and `signCreateDataSetAndAddPieces` functions from `@filoz/synapse-core/typed-data` handle the signature creation.

Check out the [Synapse Core Reference](/reference/filoz/synapse-core/toc/) for all available modules and functions.