Headless SDK
@gizmolab/depositos ships in two flavours:
- Batteries-included — drop in
<DepositOSWidget />, theme it with config, done. - Headless — import composable hooks + primitives from
@gizmolab/depositos/headlessand build your own UI and flow on top of the depositOS engine.
Use headless when you want full control of the layout/UX (custom wallet picker, custom token list, embed the flow inside your own screens) while reusing our quoting, execution, balance, and analytics plumbing.
npm install @gizmolab/depositos
Providers
Headless needs two providers: the depositOS wallet provider (wallet + email login)
wrapping the depositOS DepositOSProvider (deposit state + config). Both come from
@gizmolab/depositos — no third-party imports.
The wallet environment comes from useWalletEnvironmentId(), which resolves it
from the depositOS API against your apiKey — there's no wallet-vendor account
to set up. It returns "" until the answer arrives; don't mount the provider
before then, since it reads its environment once on mount.
import {
DepositOSWalletProvider,
EthereumWalletConnectors,
SolanaWalletConnectors,
DepositOSProvider,
useWalletEnvironmentId,
setApiKey,
} from "@gizmolab/depositos";
const config = {
destChain: 42161,
destToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", // USDC on Arbitrum
destAddress: "0xYourTreasury",
apiKey: "dos_live_…", // from the depositOS portal — attributes volume/txns to your app
};
// The wallet provider sits above DepositOSProvider, so the key is set here
// rather than picked up from config — the environment lookup is keyed on it.
setApiKey(config.apiKey);
export function Providers({ children }) {
const environmentId = useWalletEnvironmentId();
if (!environmentId) return null; // still resolving — render your own skeleton
return (
<DepositOSWalletProvider
settings={{
environmentId,
walletConnectors: [EthereumWalletConnectors, SolanaWalletConnectors],
}}
>
<DepositOSProvider config={config}>{children}</DepositOSProvider>
</DepositOSWalletProvider>
);
}
Bringing your own wallet-vendor account? Call setWalletEnvironmentId(...)
before rendering and the hook returns that value straight away, no request made.
Hooks
| Hook | Returns |
| --- | --- |
| useDepositOSState() | { config, state, dispatch, reset } — the deposit state machine. |
| useWalletTokens() | { tokens, loading, error, address, refetch } — tokens the connected wallet actually holds (non-zero, USD-sorted). |
| useQuote() | Auto-fetches a route when source token + amount are set; writes to state. |
| useExecution() | { runExecute } — approve + bridge/swap the current quote. |
| useWalletSync() | Keeps the connected wallet in sync with deposit state. |
| useDepositAnywhere(config, onSuccess?, resume?, onWithdrawReady?) | "Deposit from anywhere" flow. Returns the user's persistent deposit address (see config.userId), the chain/token pickers, and live status. |
| usePrivateSwap(config) | Private-mode swap flow. |
| useWalletEnvironmentId() | The wallet environment for DepositOSWalletProvider, resolved from your API key. "" until it arrives. |
| useApiKeyGate() | { status, reason, walletEnvironmentId, recheck } — gate your UI on a valid API key, mirroring server enforcement. |
Data primitives (framework-agnostic)
fetchWalletTokens, fetchChains, fetchTokens, fetchQuote, quoteToRoute,
executeRoute, sendEvent,
setEventsApiKey, setWalletEnvironmentId, address helpers (isEVMAddress,
isSolanaAddress, maskAddress), and SOLANA_CHAIN_ID / isSolanaChainId.
UI primitives
Compose your own screens with WalletFlow, ChainTokenSelector, TokenSelector,
AmountInput, and WalletConnect, or ignore them and build everything yourself.
Full example — a minimal custom flow
Connect wallet → list held tokens → pick one → enter amount → deposit. No prebuilt widget; just hooks.
"use client";
import { useState } from "react";
import {
useWalletTokens,
useQuote,
useExecution,
useWalletSync,
useDepositOSState,
type WalletToken,
} from "@gizmolab/depositos/headless";
export function CustomDepositFlow() {
useWalletSync(); // sync connected wallet → deposit state
useQuote(); // auto-quote when source token + amount are set
const { runExecute } = useExecution();
const { state, dispatch } = useDepositOSState();
const { tokens, loading } = useWalletTokens();
const [picked, setPicked] = useState<WalletToken | null>(null);
// 1. Not connected → render your own connect UI (or the exported WalletConnect)
if (!state /* replace with your own connected check */ && false) return null;
// 2. Pick a token the wallet already holds
if (!picked) {
if (loading) return <p>Loading balances…</p>;
return (
<ul>
{tokens.map((t) => (
<li key={`${t.chainId}:${t.address}`}>
<button
onClick={() => {
setPicked(t);
dispatch({ type: "SET_SOURCE_CHAIN", payload: t.chainId });
dispatch({
type: "SET_SOURCE_TOKEN",
payload: { address: t.address, decimals: t.decimals },
});
}}
>
{t.symbol} — {t.amountFormatted} (${t.balanceUSD.toFixed(2)})
</button>
</li>
))}
</ul>
);
}
// 3. Amount + deposit
return (
<div>
<input
value={state.amount}
onChange={(e) => dispatch({ type: "SET_AMOUNT", payload: e.target.value })}
placeholder="0.00"
/>
<button disabled={state.state !== "ready"} onClick={() => runExecute()}>
{state.state === "quoting" ? "Finding route…" : "Deposit"}
</button>
</div>
);
}
Wrap <CustomDepositFlow /> in the Providers shown above.
Analytics & your API key
Pass your app's apiKey (from the portal) in the config. Every event the
SDK emits (widget_loaded, quote_*, tx_initiated, tx_completed, tx_failed)
is sent to the depositOS API with an x-api-key header, so the portal can attribute
volume and transactions to your app. Completed deposits include a best-effort USD
value for volume reporting.