# Funding

:::warning[Beta]
This feature is experimental and may change as we iterate.
:::

For concepts (sessions, statuses, refunds) and the underlying HTTP API see [Funding](/docs/configuration/funding) and the [REST reference](/docs/configuration/funding/headless). This page covers the `openfort.funding` namespace in `@openfort/openfort-js`.

## Using the SDK

`@openfort/openfort-js` exposes funding under `openfort.funding` — usable from any JavaScript runtime (browser or server) with your publishable key.

When the source route is known upfront, funding is **one call plus a wait** — pass `paymentMethod` at creation and the session comes back with the deposit address. The SDK remembers each session's `clientSecret`, so follow-up calls don't need to thread it.

```ts
import { Openfort } from '@openfort/openfort-js'

const openfort = new Openfort({
  baseConfiguration: { publishableKey: process.env.OPENFORT_PUBLISHABLE_KEY! },
})

// Required before accessing openfort.funding (like auth / embeddedWallet / user)
// — see https://www.openfort.io/docs/products/embedded-wallet/javascript/use-openfort
await openfort.waitForInitialization()

// One call: create the session AND mint the deposit address.
// Destination: USDC on Base. Source: USDC on Polygon.
const session = await openfort.funding.sessions.create({
  target: {
    chain: 'eip155:8453',
    currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    address: '0xUserWalletAddress',
  },
  paymentMethod: {
    type: 'evm',
    source: { chain: 'eip155:137', currency: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', amount: '10000000' },
  },
})

console.log(session.status)                          // "waiting_payment"
console.log(session.paymentMethod?.receiverAddress)  // address the user sends to
console.log(session.paymentMethod?.addressUri)       // EIP-681 / Solana Pay URI for a QR

// Wait for settlement (polls until succeeded | bounced | expired).
const settled = await openfort.funding.sessions.wait(session.id, { pollMs: 4000, timeoutMs: 600000 })
console.log(settled.status) // "succeeded"
```

`wait` accepts `{ clientSecret?, pollMs?, timeoutMs? }` — `pollMs` (default `4000`) is the poll interval and `timeoutMs` (default `1800000`, 30 min) is how long to wait before giving up; `clientSecret` is filled from the remembered session when omitted.

Prefer the two-step flow when the user picks the source later — `sessions.create({ target })` then `sessions.setPaymentMethod(session.id, { paymentMethod })`; the SDK fills in the remembered `clientSecret` (pass it explicitly for sessions created elsewhere).

`create` also accepts `amountUnits` (lock the deposit to a fixed amount in destination base units), `metadata`, `externalId` (an idempotency key — reusing it returns the existing session), and `strict` (`true` mints a single-use deposit address). A session moves through `requires_payment_method` → `waiting_payment` → `processing` → `succeeded`, or ends in `bounced` / `expired`; see the [REST reference](/docs/configuration/funding/headless) for the full field list.

:::note
A `@openfort/openfort-node` server namespace is planned; until then, server-side integrations call the [REST endpoints](/docs/configuration/funding/headless#rest-endpoints) directly.
:::

## Payment method types

`setPaymentMethod` (or the inline `paymentMethod` on `create`) takes one of two source types:

:::code-group
```ts [EVM]
{ type: 'evm', 
  source: { 
  chain: 'eip155:137', 
  currency: '0x3c49…3359', 
  amount: '10000000' } 
  }
```

```ts [Solana]
{ type: 'solana', 
  source: { 
    chain: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp', 
    currency: 'EPjF…Dt1v', 
    amount: '10000000' } 
  }
```
:::

`evm` / `solana` are self-custody transfers — the response carries `receiverAddress`, `addressUri`, and wallet `deeplinks`. For every response field, see the [REST reference](/docs/configuration/funding/headless#payment-method-types). To fund from a centralized exchange instead, use `payLink`.

:::tip[Show fees and the minimum before the user sends]
Once a payment method is set, `session.paymentMethod.fees` (an array of `{ kind, amount, currency }` where `kind` is `gas` | `relayerGas` | `relayerService` | `app`) and `session.paymentMethod.minAmount` (in base units, or `null` for no floor) carry the cost and minimum for the route. Surface both so the user sees what it costs and the smallest amount they can send before they transfer.
:::

:::warning[Set `refundTo` for cross-VM routes]
A `bounced` session refunds on the **source** chain. On a cross-VM route (e.g. Solana → Base) the destination address isn't valid there, so set `refundTo` on the payment method to an address you control on the source chain — otherwise the refund can't be delivered. See [Refunds](/docs/configuration/funding/headless#refunds).
:::

## `fund()` — create and wait in one call

`openfort.funding.fund` is a shorthand for the create-and-wait flow [shown above](#using-the-sdk) — it bundles `sessions.create({ paymentMethod })` and `sessions.wait` into one call, resolving with the terminal session (`succeeded`, `bounced`, or `expired`) and rejecting on timeout.

```ts
const settled = await openfort.funding.fund({
  target: {
    chain: 'eip155:8453',
    currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    address: '0xUserWalletAddress',
  },
  paymentMethod: {
    type: 'evm',
    source: { chain: 'eip155:137', currency: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', amount: '10000000' },
  },
  wait: { pollMs: 4000, timeoutMs: 600000 }, // optional; omit to use sessions.wait defaults (4000 / 30 min)
})

console.log(settled.status) // "succeeded"
```

It takes the same session options as `create` (`amountUnits`, `metadata`, `externalId`, `strict`) plus an optional `wait` for the poll interval and timeout.

## `payLink()` — buy or transfer from an exchange

`openfort.funding.payLink` returns a prefilled Coinbase / exchange **Transfer funds** URL that settles straight to the session's wallet. It's session-bound — the destination comes from the session, so the caller only chooses the `amount` (and optionally `asset`, which defaults to `USDC`); the `clientSecret` is filled from the remembered session.

```ts
const session = await openfort.funding.sessions.create({ target })
const url = await openfort.funding.payLink({ sessionId: session.id, amount: '25' })
// → open `url` so the user can send from their exchange
```

This is a separate rail from a session payment method — use it to let a user **buy** or transfer from a centralized exchange rather than send from a self-custody wallet.

## `chains()` — list source chains

`openfort.funding.chains` returns the routable source chains and currencies live from the rail — build a source picker from it instead of hardcoding.

```ts
const chains = await openfort.funding.chains()
// → FundingChain[] — same shape as the REST /chains response
```

See [Supported chains & currencies](/docs/configuration/funding/headless#supported-chains--currencies) for the field shape and a copy-paste table of common chains.

## Next steps

* Read the full HTTP API, session options, and response fields in the [REST reference](/docs/configuration/funding/headless).
* Build a Deposit UI in React with the [`useFunding` hook](/docs/products/embedded-wallet/react/wallet/funding) or the [wallet modal](/docs/products/embedded-wallet/react/ui/configuration#funding).
* React to settlement with [Webhooks](/docs/configuration/webhooks#funding-events).
