Integration FAQ¶
Questions specific to the embed widget SDK. For general protocol questions see the top-level FAQ.
Do I need a server to run this widget?¶
No. The widget is fully client-side. You drop a script tag (or import a React component) onto your existing site and the widget renders inside an iframe. No backend, no API keys to manage.
What referralId format should I use? Hex hash or vanity slug?¶
Both work. 8-hex-char hashes (for example 6fb85213) are used directly. Anything else is treated as a vanity slug and resolved against the dapp's slug mapping to the underlying hash. Most validators paste the hash shown on their validator self-serve page on atomcircuit.net. Vanity slugs work too if you registered one. Unknown slugs are stored as-is for forward compatibility but the widget's validity check rejects them until they exist in the slug mapping.
Can my users choose which validator they support?¶
Yes. By default the referralId you pass is fixed and the end user cannot change it. Pass allowReferralChoice: true (alongside referralId) to show a validator picker inside the widget: your referralId is the pre-selected default, the user can switch to any participating validator or clear it to split across all (general), and their choice is remembered across reloads on your site. Omitting the flag (the default) keeps the fixed-referralId behaviour. See Letting users choose the validator.
Which wallets can my users connect with?¶
It depends on the connect mode, and both are fully supported. With the built-in in-widget connect (the default - no wallet option) the set is fixed: on Cosmos, Keplr and Cosmostation (each as a desktop extension and on mobile via WalletConnect); on EVM, any injected browser wallet (MetaMask, Rabby, anything exposing window.ethereum) plus WalletConnect. With parent-wallet reuse (the advanced wallet.mode: 'parent' opt-in) the widget reuses whatever wallet is connected on your page and is not limited to that set: on Cosmos, any Keplr-API-compatible injected wallet via fromInjectedCosmosWallet (window.keplr and window.cosmostation.providers.keplr are just examples) or any cosmos-kit client via fromCosmosKit; on EVM, any EIP-1193 provider via fromWagmi. See Supported wallets.
Can the widget reuse the wallet already connected on my page?¶
Optionally, yes - this is an advanced opt-in, not part of the basic setup. By default the widget connects its own wallet inside the iframe with no extra wiring, so the user connects there even if your page has a wallet connected; that default is all most integrators need. Only if your site already runs a Cosmos (cosmos-kit) or EVM (wagmi) wallet and you want to skip the reconnect, set wallet.mode: 'parent' with a cosmos and/or evm handle to reuse that connection. Parent mode trusts the page it is embedded in by that page's own origin: the widget trusts the browser's unspoofable parent origin, so the bridge works directly with nothing to register. The user keeps signing in their own wallet UI - keys never leave their wallet, and the iframe only requests signatures. The Cosmos side bridges via @dao-dao/cosmiframe, the EVM side via a custom postMessage EIP-1193 relay. Use the fromCosmosKit / fromWagmi helpers to build the handles. Trust rests on the per-message origin check (the iframe only talks to its real parent origin). If the bridge cannot be established for any reason the widget silently falls back to in-iframe connect. Omitting wallet (or mode: 'iframe', the default) is unchanged. See Reusing the parent page's wallet.
My wallet connects after the widget mounts. Do I have to delay rendering?¶
No. The recommended flow is the opposite: render the widget immediately in 'parent' mode with no handles, so the swap UI is visible the whole time, then hand the wallet over the moment your user connects on your page. With the imperative API, call the setWallet / clearWallet methods returned by mount(); in React, pass the wallet.cosmos / wallet.evm handle on a later render and the component diffs handle identity and adopts with no remount and no reconnect. If a channel is not yet bridged, supply onWalletConnectRequest(channel) to render an in-widget Connect button (your handler runs your own connect flow, then calls setWallet), or omit it for a passive prompt whose text you can override with connectPrompt. See Connecting after mount and In-widget connect button.
The embedded widget doesn't react when the host page connects its wallet (parent mode).¶
This is almost always a bad cosmos handle. The usual cause is building the cosmos handle from useChain().client - cosmos-kit's useChain() has no top-level client field, so that value is always undefined, fromCosmosKit(undefined) never produces a usable handle, and setWallet never fires, so the iframe stays on its own in-iframe connect. The wallet client lives at chainWallet.client and is populated only after the user connects. Fix it by deriving the client from chainWallet.client and keying your memo on the client identity (which appears asynchronously after connect) so the handle is rebuilt the moment it exists:
import { useChain } from '@cosmos-kit/react';
import { fromCosmosKit } from '@atom-circuit/embed-sdk';
const { status, chainWallet } = useChain('cosmoshub');
// useChain() has no top-level `client` - the wallet client is chainWallet.client (only after connect).
const cosmosClient = chainWallet?.client;
const cosmos = useMemo(
() => (status === 'Connected' && cosmosClient ? fromCosmosKit(cosmosClient) : undefined),
[status, cosmosClient] // key on the client identity - it appears asynchronously after connect
);
return <AtomCircuitSwap referralId="..." wallet={{ mode: 'parent', cosmos }} />;
See Reusing the parent page's wallet for the full parent-mode guide.
Can the widget access my page's data (cookies, storage, DOM)?¶
No. The iframe runs in a sandbox with allow-same-origin (required for Keplr to inject window.keplr) but the cross-origin browser boundary prevents the widget from reading the host page's DOM, cookies, or localStorage. The exact sandbox attribute list is defined in src/mount.ts in the SDK repo.
Does the widget work on mobile?¶
Yes for the Keplr in-app browser and for most standalone mobile browsers via WalletConnect. Cold-start signing in some mobile browsers (notably iOS Safari with WalletConnect) can time out after 90 seconds; the widget surfaces this as a "Signing timed out, copy link to Keplr app" prompt.
The widget looks small on a large desktop screen. How do I make it bigger?¶
Set autoscale: true. The widget is designed around a natural width of 480px, so in a wider container the default leaves it at that density with the surrounding space empty. With autoscale on, the embed scales its entire geometry up proportionally (text, buttons, icons, inputs, and spacing all grow together) to fill the available width. Use maxScale (default 1.5, clamped to [1.0, 3.0]) to cap how large it gets. Below 480px of available width it does nothing and falls back to the normal fluid layout, so narrow and mobile viewports are unaffected. It is off by default and backward compatible. See the Autoscale section for the full behaviour.
Can I change the widget's colors, fonts, and corner roundness?¶
Yes. Pass a theme object. As of 2.1.0 the surface covers mode ('dark' default, 'light', 'auto'), the colors accentColor, accentForeground, background, foreground, card, cardSecondary, input, mutedForeground, border, borderFocus, warning, success, error (all hex), plus radius, fontSize, and fontFamily. The surface tokens nest: card is a convenience bundle that sets every surface (card, secondary panel, input, validator band), while cardSecondary overrides only the secondary / band tier and input overrides only the input surface, each winning over card. fontSize is applied at the iframe root so it scales the whole widget, and maxWidth caps the swap form, not just the iframe. Pick a preset with mode, then override individual tokens on top of it. See the Embed Widget page for the full token table and a themed example.
Can I hide the logo or wallet button?¶
Yes. Pass chrome: { logo: false, wallet: false, validator: false, footer: false }. Each flag defaults to true, so a no-config embed shows the full chrome. See the Embed Widget page for the full chrome surface.
Where do swap fees go and how much?¶
The current affiliate fee is 50 bps (0.5%) of swap output. The fee routes through Skip Go to the protocol's collector wallets, gets converted to ATOM on Cosmos Hub, and is delegated to the validator whose referralId you passed at mount time. See the Fee Flow page for the full pipeline.
What if my referralId is wrong, missing, or stale?¶
If referralId is missing, the widget renders an inline "Embed configuration error" message and refuses to render the swap form. If it is present but does not resolve to a known validator (not the 8-hex pattern, not the general sentinel, not a registered slug), the widget renders an "Invalid referral" message instead. Fees never route to an unknown validator. Test your referralId on a staging copy of your page before publishing.
Does the widget make any third-party requests beyond the swap API?¶
Only requests originated by the dapp loaded inside the iframe (Skip Go for routing, Cosmos REST endpoints for balances, the chain registry for asset metadata). The SDK itself does not make analytics, telemetry, or tracking calls. Verify by inspecting Network DevTools after onReady fires.
How do I get notified when a user completes a swap?¶
Pass onSwapSuccess to the SDK. It fires once per successful cross-chain delivery with { txHash } (the source-chain transaction hash). For multi-step swaps you can also pass onSwapBridging, which fires while the bridge leg is still settling (non-terminal - onSwapSuccess or onSwapError still fires afterward) with { chainId, explorerLink? }. See the Embed Widget Callbacks section for the full event list.
Why is my widget still showing an old version after I updated the npm package?¶
If you embed via the unpkg CDN, browser cache or unpkg cache can hold the previous bundle for several minutes. Either bump your pinned version (bump the patch suffix) or use a cache-busting query param (?v=<git-sha>). If you bundle via npm install, run npm update and rebuild.
What browsers are supported?¶
Chromium 115+, Firefox 115+, Safari 16+. iOS Safari 16+ and Android Chrome 115+ on mobile. The widget targets the ES2020 baseline; older browsers without that baseline are not supported.
Where do I report bugs or request features?¶
Open an issue on the SDK repository: github.com/cosmosrescue/atom-circuit-embed-sdk/issues. For security issues, follow the private disclosure process documented in SECURITY.md on the repo.
How do I know the SDK on unpkg is the same as the published npm package?¶
Use Subresource Integrity. The release publishes a sha384-... hash for the IIFE bundle; the browser refuses to execute the bundle if the served bytes do not match the hash. See the Security Model section in the Embed Widget page for the script tag form.