For the complete documentation index, see llms.txt. This page is also available as Markdown.

Install & Embed

Four ways onto the page — React, mount(), a script tag, or Next.js — plus the compatibility floor.

npm i @glacislabs/stablegenie-zerodelta

react and react-dom (>= 18) are peer dependencies for the main entry — they are kept external in the ESM and CJS builds, so your React is reused and nothing is double-bundled. The package itself has zero runtime dependencies.

CSS is auto-injected. There is no stylesheet to import. The widget writes its own styles into the document on first render, scoped to its own root.

You also need the keyless proxy. The widget's default apiBase is a same-origin endpoint you deploy, which injects the ZeroDelta key server-side. Without it every request answers 503 proxy_misconfigured. This is not optional — see The Keyless Proxy.

Entry points

Import
Contains
React required

@glacislabs/stablegenie-zerodelta

the React components, mount, the theme helpers, createEngine

yes

…/core

the framework-agnostic engine — quoting, routing, encoding, the pre-sign validator, the on-chain verifier

no

…/mount

imperative mounting for non-React hosts

yes (bundled by your build)

…/standalone

the prebuilt IIFE with React bundled in, for plain <script> embeds

no

Use …/core if you want the routing and validation engine without pulling in React — it is verifiably React-free.

React

import { StableGenieExchange } from "@glacislabs/stablegenie-zerodelta";

// `provider`: your app's EIP-1193 provider (wagmi connector, window.ethereum…) so the user does not connect twice.
<StableGenieExchange
  variant="wide"
  defaultFromChainId={42161}
  defaultFromAsset="USDT"
  defaultToChainId={8453}
  defaultToAsset="USDC"
  provider={provider}
/>

An exact-receive deposit instead:

Imperative mount()

For React hosts that do not write JSX at the embed point.

variant means two different things, and this is the sharpest footgun in the package. In mount() it selects the component — "pay" (default), "swap", "exchange" or "rwa". In a component it selects the layout — "compact", "wide" or "pill". mount() strips its own selector before rendering, so it cannot express a layout: there is no way to get a pill or wide card through mount(). Use JSX, or the script-tag helpers below, which pass the display variant through.

Plain <script>

No React on the host at all. The standalone build bundles its own.

VERSION is the exact release you pin. The current one is on the package's npm page, and the playground's Script tag tab prints this line with it already filled in.

The global exposes five helpers, each taking the same props as the matching React component — including the display variant — and returning an unmount function:

Helper
Renders

mountPay(el, props)

StableGeniePay

mountSwap(el, props)

StableGenieSwap

mountExchange(el, props)

StableGenieExchange

mountRwaExchange(el, props)

StableGenieExchange with product: "rwa"

mountExpress(el, props)

PaymentIsland — ZeroDelta Link

jsDelivr works too: https://cdn.jsdelivr.net/npm/@glacislabs/stablegenie-zerodelta@VERSION/dist/standalone.global.js

Pin an exact version, and prefer self-hosting if your CSP disallows third-party script origins — this widget sits in a payment path. The file ships inside the package at dist/standalone.global.js, so you can copy it into your own static assets at build time:

Next.js and server rendering

The widget is a client component. Importing it on the server does not crash — style injection, localStorage and wallet discovery are all guarded on typeof window — but it renders nothing useful there.

  1. The package ships no "use client" directive. Wrap it in your own client component:

  1. Or skip SSR entirely where you would rather not ship it in the server bundle:

  1. Route the proxy as a Next handler. Mount it at app/api/widget/v1/[...path]/route.ts so the widget's default apiBase resolves without configuration. The proxy logic is plain data in and plain data out — see The Keyless Proxy for a complete handler.

Compatibility

React

18 and 19 (peer dependency)

Module formats

ESM + CJS, with .d.ts and .d.cts types

Bundlers

Vite, webpack, Next.js, Rollup, esbuild — nothing special required

Node

>= 20, for building only

Runtime dependencies

none

Licence

MIT

Browser APIs used: fetch, BigInt, URL / URLSearchParams, Promise.all, CustomEvent, and localStorage (guarded — it degrades cleanly in private or sandboxed contexts). Any browser with those needs no polyfill; there is no viem or ethers to shim because neither is used.

When something does not render

Symptom
Cause

Every request answers 503 proxy_misconfigured

The proxy has no STABLEGENIE_API_KEY set

403 origin_not_allowed

Cross-origin embed with the origin missing from WIDGET_ALLOWED_ORIGINS

Card renders but no chains or tokens load

The /api/widget/v1/* route is not mounted or not rewritten to your handler

Direct settlement never appears as a route

Your proxy does not allow screen/wallet — the engine cannot screen, so it fails closed. See The Keyless Proxy

A payment succeeds on-chain but onSuccess never fires

Your CSP connect-src does not allow the verification RPC — see The Keyless Proxy

variant: "pill" is ignored

You used mount(), which cannot express a display variant

Try a configuration first

The widget playground renders every component against the production gateway and prints the exact React, mount() and <script> snippet for whatever you configure. The snippets on this page are the same ones it generates.