@socket.tech/widget
v1.0.5
Published
Socket Widget is a swap and bridge widget for moving tokens across blockchains. The `@socket.tech/widget` package exposes a React component you can embed in your app.
Readme
Socket Widget
What is Socket Widget?
Socket Widget is a swap and bridge widget for moving tokens across blockchains. The @socket.tech/widget package exposes a React component you can embed in your app.
- Live product: bungee.exchange
- NPM package: @socket.tech/widget
- Documentation: docs.socket.tech
v1.0.0: The widget is wallet-provider-agnostic. Your app provides a minimal wallet adapter via
config.wallet, and the widget handles routing, reads, writes, balances, receipts, and signing internally.
Before You Start
Socket Widget handles swap and bridge flows, while your app owns wallet connection.
- Provide wallet state and wallet connect UI.
- Mount
QueryClientProviderabove<SocketWidget />. - Pass a minimal wallet adapter via
config.wallet. - Support EVM only, Solana only, Tron only, or any combination by providing the matching adapter methods. You only need to pass the methods for the chains you support — e.g. for EVM-only apps, provide
getEVMWalletClientandswitchChain; you do not need to passgetSolanaSignerorgetTronWeb.
Minimal React Query setup:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);Install
Install the package and its peer dependencies:
pnpm install @socket.tech/widget react react-dom viem @tanstack/react-queryPeer dependencies are not installed automatically.
This installs the widget, not a wallet/connect solution. You must already have, or choose, wallet infrastructure in your app.
Import the widget styles in your app entrypoint:
import "@socket.tech/widget/styles.css";
import "@socket.tech/widget/fonts.css";Quickstart
For a transaction-capable integration, the setup is:
- Install the package and peer dependencies.
- Mount
QueryClientProvider. - Import the widget CSS.
- Add a minimal wallet adapter.
- Render
<SocketWidget config={config} />.
Choose the smallest path that matches your app:
- EVM only: provide
accounts,getEVMWalletClient, and usuallyswitchChain. You do not need to passgetSolanaSignerorgetTronWeb. - Solana only: provide
accountsandgetSolanaSigner. You only needrpcs.solanaif you want to override the default Solana RPC. - Tron: provide
accounts(including a TRON account) andgetTronWeb. - EVM + Solana: combine EVM and Solana adapters in the same
walletobject. - EVM + Tron: add
getTronWeband TRON accounts for Tron-supported routes.
EVM-Only Example
This is the smallest recommended setup if your app already uses an EVM wallet library such as wagmi.
Your connect modal or connect button still belongs to your app.
import { SocketWidget, type WidgetConfig } from "@socket.tech/widget";
import { getWalletClient } from "@wagmi/core";
import { useMemo } from "react";
import { useAccount, useConfig, useSwitchChain } from "wagmi";
export function BungeeWidget() {
const { address, chain, isConnected } = useAccount();
const { switchChainAsync } = useSwitchChain();
const wagmiConfig = useConfig();
const accounts = useMemo(
() =>
address && isConnected
? [
{
address,
chainType: "EVM" as const,
chainId: chain?.id,
isConnected: true,
},
]
: [],
[address, chain?.id, isConnected],
);
const config: WidgetConfig = {
affiliateId: "your-affiliate-id",
wallet: {
accounts,
getEVMWalletClient: async (chainId) => {
return getWalletClient(wagmiConfig, { chainId });
},
switchChain: async (chainId) => {
await switchChainAsync({ chainId });
return true;
},
},
eventHandlers: {
onOpenWalletConnect: () => {
// Open your app's wallet modal or navigate to your connect screen.
openYourWalletModal();
},
},
};
return <SocketWidget config={config} />;
}Solana-Only Example
This example assumes Solana wallet-adapter providers are already mounted higher in your app.
import { SocketWidget, type WidgetConfig } from "@socket.tech/widget";
import { useMemo } from "react";
import { useWallet } from "@solana/wallet-adapter-react";
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
export function BungeeSolanaWidget() {
const wallet = useWallet();
const { setVisible } = useWalletModal();
const accounts = useMemo(
() =>
wallet.publicKey
? [
{
address: wallet.publicKey.toBase58(),
chainType: "SVM" as const,
isConnected: wallet.connected,
},
]
: [],
[wallet.connected, wallet.publicKey],
);
const config: WidgetConfig = {
affiliateId: "your-affiliate-id",
rpcs: {
solana: "https://api.mainnet-beta.solana.com",
},
wallet: {
accounts,
getSolanaSigner: async () => {
if (!wallet.connected) return null;
return {
signAndSendTransaction: wallet.sendTransaction ?? undefined,
signTransaction: wallet.signTransaction ?? undefined,
};
},
},
eventHandlers: {
onOpenWalletConnect: () => {
setVisible(true);
},
},
};
return <SocketWidget config={config} />;
}To support both EVM and Solana, combine this signer with the EVM adapter shown above in the same wallet object.
Both EVM and Solana
If your app supports both networks:
- pass both account types in
wallet.accounts - provide both
getEVMWalletClientandgetSolanaSigner - keep
switchChainfor EVM - route
eventHandlers.onOpenWalletConnect(network)to the correct host wallet modal
Adding Tron (optional)
To support Tron deposit or direct-deposit flows:
- include TRON accounts in
wallet.accounts(withchainType: "TRON") - provide
getTronWebreturning your app's TronWeb instance (must satisfy theTronWebLikeinterface) - route
eventHandlers.onOpenWalletConnect("tron")to open your Tron wallet/connect UI
If you do not pass getTronWeb, the widget works as before; Tron routes simply won't be available for sending.
Configuration Reference
Widget Config Interface
Required Properties
wallet(WalletAdapter): Minimal wallet adapter for connected-wallet flows. Required. See Wallet Adapter.affiliateId(string): Integrator tracking identifier. Required for every widget instance.
Optional Properties
baseUrl(string): API root URL. If not passed, the widget useshttps://backend.socket.tech/v3.turnstileSiteKey(string): Cloudflare Turnstile site key. When provided, the widget loads Turnstile and uses JWT auth for requests. When omitted, the widget uses affiliate-only requests.theme(Theme): Theming configuration. See Theme Configuration.widgetTitle(string): Optional title shown in the widget header. Default is"Swap". Only set if you need to customize the header label.rpcs(object): Custom RPC URLs (all optional)solana(string): Solana RPC URL. Optional. The widget defaults tohttps://api.mainnet-beta.solana.com; pass this only if you want to override it.
initialValues(object): Default valuesfromChain(number): Default source chain IDtoChain(number): Default destination chain IDinputTokens(string[]): Default input token addressesoutputTokens(string[]): Default output token addressesamount(string[]): Default amounts
supportedTokens(object): Restrict available tokens. See Token Configuration Details.from(object): Tokens by chain ID for source. Structure:{ [chainId: string]: Token[] }to(object): Tokens by chain ID for destination. Structure:{ [chainId: string]: Token[] }
excludedTokens(object): Hide specific tokens from token lists and token search.from(object): Token addresses to hide in the source selector. Structure:{ [chainId: number]: string[] }to(object): Token addresses to hide in the destination selector. Structure:{ [chainId: number]: string[] }
supportedChains(object): Restrict available chainsfrom(number[]): Available source chainsto(number[]): Available destination chains
eventHandlers(EventHandlers): Event callbacks. See Event Handlers.feeParams(object): Integrator fee configurationfeeTakerAddress(string): Wallet address that receives the feefeeBps(number): Fee in basis points (for example10 = 0.1%)
features(object): Feature flagsinternalToasts(boolean): Show or hide internal toasts (default:true)internalTxHistory(boolean): Show or hide transaction history (default:true)internalInflight(boolean): Show or hide the inflight screen as a non-blocking toast (default:true)internalTokenSelector(boolean): Render the built-in token selector, or delegate token selection to your app viaeventHandlers.onOpenTokenSelector(default:true)internalTokenSelectorBounds("widget"|"viewport"): Constrain the built-in token selector to the widget or viewport (default:"widget")
Base URL and affiliate requirements
The widget uses a single default backend root: https://backend.socket.tech/v3.
- If you do not pass
baseUrl, the widget uses the Socket backend URL above. affiliateIdis required in every config.turnstileSiteKeyis optional. Pass it only if you want JWT-authenticated requests.- You may still override
baseUrlexplicitly if you need to point the widget at a different compatible backend.
Wallet Adapter
The wallet object is the only host integration point the widget needs. You provide connected accounts and the network-specific wallet hooks you support; the widget handles the rest internally.
| Property | Type | Required | Description |
|---|---|---|---|
| accounts | AccountInfo[] | Yes | Connected wallet accounts |
| getEVMWalletClient | (chainId: number) => Promise<WalletClient \| null> | EVM only | Returns a viem WalletClient for the requested chain |
| getSolanaSigner | () => Promise<SolanaSigner \| null> | Solana only | Returns a Solana signer for signing transactions |
| getTronWeb | () => TronWebLike \| undefined | Tron only | Returns the wallet-connected TronWeb instance for Tron transactions and deposit flows |
| switchChain | (chainId: number, account?) => Promise<boolean> | Recommended for EVM | Switches the wallet's active EVM chain |
What the widget handles internally
When you provide getEVMWalletClient, the widget automatically implements:
sendTransaction(including EIP-5792 batched calls)signTypedDatawriteContractreadContract(via viem public clients)getBalance(native + ERC-20)getTransactionReceiptgetBytecodegetEnsAddressgetWalletCapabilities(EIP-5792)getCallsStatus(EIP-5792)
When you provide getSolanaSigner and config.rpcs.solana, the widget additionally handles:
- Solana transaction building and sending (VersionedTransaction with lookup tables)
- SOL and SPL token balance fetching (including Token-2022)
- Solana transaction receipt polling
When you provide getTronWeb, the widget additionally handles:
- Tron transaction building, signing, and broadcasting (for deposit and direct-deposit flows)
- TRX and TRC-20 balance and transaction receipt via the host-supplied TronWeb instance
accounts
An array of currently connected wallet accounts:
accounts: Array<{
address: string;
chainType: "EVM" | "SVM" | "TRON";
chainId?: number;
isConnected: boolean;
connector?: {
id?: string;
name?: string;
icon?: string;
};
}>getEVMWalletClient
Returns a viem WalletClient for the requested chain. The widget uses this to send transactions, sign typed data, write contracts, and query EIP-5792 capabilities.
getEVMWalletClient: (chainId: number) => Promise<WalletClient | null>The return type is any in the adapter interface so hosts are free to use any viem version; the widget casts internally.
getSolanaSigner
Returns a Solana signer object. The widget builds the transaction internally (instructions, lookup tables, blockhash) and passes it to the signer. If the signer provides signAndSendTransaction, the widget prefers it; otherwise it falls back to signTransaction + sendRawTransaction via its internal Connection.
getSolanaSigner: () => Promise<{
signAndSendTransaction?: (...args: any[]) => Promise<any>;
signTransaction?: (...args: any[]) => Promise<any>;
} | null>getTronWeb
Returns the wallet-connected TronWeb instance from your app. The widget uses it to build, sign, and broadcast Tron transactions for deposit and direct-deposit flows. The instance must implement the minimal TronWebLike interface (see type export). If you do not support Tron, omit this property.
getTronWeb: () => TronWebLike | undefinedswitchChain
Switches the wallet's active EVM chain. The optional account parameter targets a specific wallet when multiple are connected.
Treat this as a success-or-throw contract in practice:
- resolve when the chain switch succeeds
- reject or throw if the switch is cancelled or fails
switchChain: (
chainId: number,
account?: { address?: string; chainType: "EVM" | "SVM" | "TRON" }
) => Promise<boolean>Token Configuration Details
Token Interface
When using supportedTokens, tokens must follow this structure:
interface Token {
address: string;
chainId: number;
decimals: number;
logoURI: string;
name: string;
symbol: string;
isVerified?: boolean; // Must be true to avoid warnings
}Using supportedTokens
Important notes:
- When
supportedTokensare provided, the widget will not automatically set a default token. - To show a default source token, you must pass
initialValues.fromChainandinitialValues.inputTokens. - Set
isVerified: truefor tokens insupportedTokensto avoid warnings. inputTokensaddresses must match tokens from yoursupportedTokens.from[chainId]list.
Using excludedTokens
Use excludedTokens when you want the widget to keep its normal token universe, but hide specific tokens from:
- the token list
- built-in search results
- external token selector payloads
Example:
excludedTokens: {
from: {
1: [
"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"0xdac17f958d2ee523a2206206994597c13d831ec7",
],
},
to: {
10: ["0x4200000000000000000000000000000000000006"],
},
}Notes:
- Matching is case-insensitive.
fromandtoare applied independently.- If you need a strict allowlist instead of a blocklist, use
supportedTokens.
Fetching Token Data
When using supportedTokens, you can fetch token data from the Socket swap API endpoints:
/swap/tokens/list: Returns a curated list of trending tokens (verified) or the complete token list/swap/tokens/search: Search for tokens by address, name, or symbol
Tokens fetched from these endpoints include the isVerified field. For detailed request and response formats, see the token-list API documentation.
Theme Configuration
Customize the widget appearance with the theme property:
const theme = {
width: 420, // or "full" for full width
borderRadius: "base", // "none" | "sm" | "base" | "md" | "lg"
fonts: {
primary: "Inter, sans-serif",
secondary: "Roboto, sans-serif",
},
colors: {
text: {
primary: "#FFFFFF",
secondary: "#A0A0A0",
button: "#000000",
theme: "#3B82F6",
accent1: "#10B981",
accent2: "#F59E0B",
accent3: "#EF4444",
accent4: "#8B5CF6",
},
bg: {
layer1: "#1F2937",
layer2: "#374151",
layer3: "#4B5563",
accent1: "#10B981",
accent3: "#EF4444",
accent4: "#8B5CF6",
main: "#111827",
theme: "#3B82F6",
},
border: {
strong: "#6B7280",
theme: "#3B82F6",
},
icon: {
primary: "#FFFFFF",
secondary: "#A0A0A0",
theme: "#3B82F6",
},
},
};Colors support both hex (#FFFFFF) and RGB (rgb(255, 255, 255)) formats.
Event Handlers
Handle user interactions and widget events:
const eventHandlers = {
onFromTokenChange: (token: Token) => {
console.log("Source token changed:", token);
},
onToTokenChange: (token: Token) => {
console.log("Destination token changed:", token);
},
onFromChainChange: (chain: Chain) => {
console.log("Source chain changed:", chain);
},
onToChainChange: (chain: Chain) => {
console.log("Destination chain changed:", chain);
},
onEvent: (eventType: "success" | "error" | "warning", eventData: EventData) => {
console.log("Widget event:", eventType, eventData);
},
onOpenWalletConnect: (network: "solana" | "eip155" | "tron") => {
openWalletModal(network);
},
onSwapInitiated: (data: { chainId: number; hash: string; type: OrderFlowType }) => {
console.log("Swap initiated:", data);
},
onTransactionStatusChange: (data: { requestHash: string; statusCode: SwapStatus }) => {
console.log("Transaction status:", data);
},
onOpenTokenSelector: (data: TokenSelectorOpenData) => {
openExternalTokenSelector(data);
},
onTokenSelectorDataChange: (data: TokenSelectorOpenData) => {
syncExternalTokenSelector(data);
},
logEvent: (log: { event: Event; error: unknown; context?: Record<string, unknown> }) => {
console.log("Analytics event:", log);
},
toggleExternalHistorySection: () => {
toggleHistorySidebar();
},
showToast: (title, message, type, duration) => {
// Display a custom toast notification
},
};Imperative API
The imperative API allows parent components to control widget state programmatically.
Use case example: when a user clicks a history item in a sidebar, the parent can navigate the widget to the inflight screen for that transaction.
import { useRef } from "react";
import { SocketWidget, type WidgetImperativeAPIType } from "@socket.tech/widget";
export default function App() {
const bungeeRef = useRef<WidgetImperativeAPIType>(null);
const handleSetInflightData = (data: OrderData) => {
bungeeRef.current?.setInflightData(data);
};
return <SocketWidget config={config} ref={bungeeRef} />;
}Available methods:
setInflightData(data: OrderData): Track pending transactions and navigate to inflight screenopenQrDeposit(quoteId: string): Open the QR deposit screen for a quoteselectToken(token: Token, isSource: boolean): Programmatically select the source or destination tokensetSourceWalletMode(mode: SourceWalletMode): Update the source wallet mode used by the widget
Type Exports
import type {
WidgetConfig,
WidgetImperativeAPIType,
WalletAdapter,
TronWebLike,
} from "@socket.tech/widget";Troubleshooting
React Query error or
No QueryClient setMountQueryClientProviderabove<SocketWidget />.Clicking connect does nothing Implement
eventHandlers.onOpenWalletConnect.Transactions fail because no wallet client is available for the selected chain Make sure
getEVMWalletClient(chainId)returns a client for the requested chain, not only the currently active chain.Chain switch handling feels inconsistent Make
switchChainreject or throw on cancellation or failure instead of silently resolving unsuccessfully.Solana actions fail immediately Mount Solana wallet-adapter providers higher in the app and pass both
getSolanaSignerandrpcs.solana.Tron deposit/direct-deposit fails or is unavailable Pass
getTronWebreturning a TronWeb instance that implementsTronWebLike, and include TRON accounts inwallet.accounts. If you do not need Tron, you can omitgetTronWeb.Widget has no styling Import
@socket.tech/widget/styles.cssand@socket.tech/widget/fonts.css.
Links
- Web App: bungee.exchange
- Widget Package: @socket.tech/widget
- Socket Widget Docs: docs.socket.tech
