Skip to main content
These hooks let you build custom UIs on top of the Spice Flow SDK. If the prebuilt components don’t fit your use case, use these hooks to access wallet state, balances, and execution directly.

useSpiceExecution

The core execution hook. Handles EIP 7702 delegation signing, intent submission, and solver execution polling. This is what powers SpiceDeposit under the hood.

Usage

import { useSpiceExecution } from "@spicenet-io/spiceflow-ui";

function CustomDeposit() {
  const { executeGasless } = useSpiceExecution();

  const handleExecute = async () => {
    const actionId = await executeGasless(
      chainBatches,           // destination chain calls
      tokenAddress,           // token being deposited
      tokenTransferAmount,    // amount in wei
      (progress) => {         // progress callback
        console.log(progress.step, progress.message);
      },
    );
    console.log("Action completed:", actionId);
  };

  return <button onClick={handleExecute}>Execute</button>;
}

Return Value

{
  executeGasless: (
    chainBatches: ChainBatch[],
    tokenAddress: string,
    tokenTransferAmount: bigint,
    onProgress?: (progress: GaslessProgress) => void,
    signal?: AbortSignal,
    options?: GaslessExecutionOptions,
  ) => Promise<string>  // returns actionId
}

Progress Steps

The onProgress callback receives updates as execution proceeds:
StepDescription
idleNot started
buildingBuilding the intent
signing-delegationUser signing EIP 7702 delegation
signing-intentUser signing the intent hash
submittingSubmitting to the solver
executingSolver executing on chain
successExecution complete
errorSomething went wrong

useSpiceAssets

Fetch the user’s Spice balance from the relayer API. This returns the off chain balance (what the user has deposited via escrow), not on chain wallet balances.

Usage

import { useSpiceAssets } from "@spicenet-io/spiceflow-ui";

function BalanceDisplay() {
  const { assets, loading, hasBalance, refetch, getAssetsByChain } = useSpiceAssets({
    address: "0x...",
    supportedChains: [8453, 4114],
    refetchInterval: 30000,
  });

  if (loading) return <p>Loading...</p>;
  if (!hasBalance) return <p>No deposits yet</p>;

  return (
    <ul>
      {assets.map(asset => (
        <li key={`${asset.chainId}-${asset.address}`}>
          {asset.symbol}: {asset.balanceFormatted} on chain {asset.chainId}
        </li>
      ))}
    </ul>
  );
}

Config

ParamTypeDefaultDescription
addressstringWallet address to query
supportedChainsnumber[]Filter to specific chains
enabledbooleantrueEnable or disable fetching
refetchIntervalnumberAuto refetch interval in ms

Return Value

FieldTypeDescription
assetsAsset[]Array of deposited assets with balances
loadingbooleanTrue during initial load
errorstring | nullError message if fetch failed
hasBalancebooleanWhether user has any balance
refetch() => Promise<void>Manually refetch balances
getAssetsByChain(chainId) => Asset[]Filter assets by chain

useWallet

Access wallet connection state and signing actions. Works with both Privy and Dynamic providers.

Usage

import { useWallet } from "@spicenet-io/spiceflow-ui";

function WalletInfo() {
  const { address, isConnected, isAuthenticated, provider, actions } = useWallet();

  if (!isConnected) return <p>Not connected</p>;

  return (
    <div>
      <p>Connected via {provider}: {address}</p>
      <button onClick={() => actions.signMessage("Hello")}>Sign Message</button>
    </div>
  );
}

Return Value

FieldTypeDescription
isReadybooleanProvider is initialized
isAuthenticatedbooleanUser has authenticated
isConnectedbooleanWallet is connected
addressAddress | undefinedConnected wallet address
provider"privy" | "dynamic" | nullActive provider
actions.signMessage(message) => Promise<{signature}>Sign a message
actions.signAuthorization(params) => Promise<Authorization>Sign EIP 7702 authorization

useEmbeddedWalletAddress

Get the Privy embedded wallet address. Returns undefined if no embedded wallet exists or in non 7702 mode.

Usage

import { useEmbeddedWalletAddress } from "@spicenet-io/spiceflow-ui";

function EmbeddedWallet() {
  const embeddedAddress = useEmbeddedWalletAddress();

  if (!embeddedAddress) return <p>No embedded wallet</p>;
  return <p>Embedded: {embeddedAddress}</p>;
}

useAssetInput

Manage asset selection and amount input state. Used to control the asset selector in deposit and lock flows from your own UI.

Usage

import { useAssetInput } from "@spicenet-io/spiceflow-ui";

function CustomInput() {
  const { selectedAsset, setSelectedAsset, assetAmount, setAssetAmount } = useAssetInput();

  return (
    <input
      type="text"
      value={assetAmount}
      onChange={(e) => setAssetAmount(e.target.value)}
      placeholder="Amount"
    />
  );
}

Return Value

FieldTypeDescription
selectedAssetSelectedAsset | nullCurrently selected asset
setSelectedAsset(asset) => voidSet the selected asset
assetAmountstringCurrent amount string
setAssetAmount(amount) => voidSet the amount

useLockDuration

Manage lock duration selection state for the SpiceLockModal.

Usage

import { useLockDuration } from "@spicenet-io/spiceflow-ui";

const durationOptions = [
  { label: "1 Month", months: 1 },
  { label: "6 Months", months: 6 },
  { label: "12 Months", months: 12 },
];

function DurationPicker() {
  const { selectedDurationIdx, setSelectedDurationIdx, selectedDuration } = useLockDuration(durationOptions);

  return (
    <div>
      {durationOptions.map((opt, i) => (
        <button key={i} onClick={() => setSelectedDurationIdx(i)}>
          {opt.label} {i === selectedDurationIdx ? "(selected)" : ""}
        </button>
      ))}
    </div>
  );
}

useSpicePendingDeposits

Detect funds stranded in the user’s embedded wallet (deposits that were sent but not yet credited to Spice balance). Used by SpiceDeposit internally for recovery flows.

Usage

import { useSpicePendingDeposits } from "@spicenet-io/spiceflow-ui";

function PendingCheck() {
  const { pendingDeposits } = useSpicePendingDeposits({
    chainIds: [8453, 4114],
  });

  if (pendingDeposits.length > 0) {
    return <p>You have {pendingDeposits.length} pending deposit(s) to recover.</p>;
  }
  return null;
}

useSpiceBrand

Resolve the current theme from the provider’s brand prop and any component level styles/dark overrides. Only used when building custom components that need to match the SDK’s look.

Usage

import { useSpiceBrand } from "@spicenet-io/spiceflow-ui";

function CustomCard({ styles, dark }) {
  const { primaryColor, dk, palette } = useSpiceBrand(styles, dark);

  return (
    <div style={{
      backgroundColor: palette.cardBg,
      color: palette.textPrimary,
      border: `1px solid ${palette.cardBorder}`,
    }}>
      Themed card
    </div>
  );
}

Return Value

FieldTypeDescription
darkbooleanWhether dark mode is active
primaryColorstringBrand accent color
themeThemeFull theme object
dkResolvedDarkPaletteDark palette values
paletteResolvedDarkPaletteMode resolved palette (light or dark)

Next Steps

Components

Use prebuilt components for common flows

Styling

Customize appearance with the brand system

API Reference

Explore the REST API for advanced use cases

Configuration

Configure chains and providers