@socket.tech/bungee
v1.0.0
Published
Bungee is a swap and bridge widget for moving tokens across blockchains. The `@socket.tech/bungee` package exposes a React component you can embed in your app.
Downloads
854
Readme
@socket.tech/bungee
What is Bungee?
Bungee is a swap and bridge widget for moving tokens across blockchains. The @socket.tech/bungee package exposes a React component you can embed in your app.
- Live product: bungee.exchange
- NPM package: @socket.tech/bungee
- Documentation: docs.bungee.exchange
v1.0.0 Breaking Change: The widget no longer bundles wagmi or a wallet provider. 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
Bungee is a widget, not a full wallet/connect solution.
- Provide wallet state and wallet connect UI.
- Mount
QueryClientProviderabove<Bungee />. - 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/bungee 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/bungee/styles.css";
import "@socket.tech/bungee/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
<Bungee 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
accounts,getSolanaSigner, andrpcs.solana. - Tron only (for deposit/direct-deposit flows): 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 { Bungee, type BungeeConfig } from "@socket.tech/bungee";
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: BungeeConfig = {
// apiKey is optional when using the default public backend; required for dedicated backend
apiKey: "your-api-key",
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 <Bungee config={config} />;
}Solana-Only Example
This example assumes Solana wallet-adapter providers are already mounted higher in your app.
import { Bungee, type BungeeConfig } from "@socket.tech/bungee";
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: BungeeConfig = {
// apiKey is optional when using the default public backend; required for dedicated backend
apiKey: "your-api-key",
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 <Bungee 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
BungeeConfig Interface
Required Properties
wallet(WalletAdapter): Minimal wallet adapter for connected-wallet flows. Only this property is mandatory. See Wallet Adapter.
Optional Properties (required by backend type)
apiKey(string): Your Bungee API key. Optional when using the default public backend. Required when using the dedicated backend URL.baseUrl(string): API base URL. If not passed, the widget uses the public backend:https://public-backend.bungee.exchange/api/v1. See Base URL and API key / affiliate requirements.affiliateId(string): For integrator tracking. Optional for the public backend. Required for the backend and dedicated URLs.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. Only needed if you want Solana support and passgetSolanaSigner.eip155(string): Custom Ethereum Mainnet RPC URL for transactions.
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 amountsoutputRatio(number[]): Output ratioslocale(SupportedLocale): UI locale (currently only"en-US")sortPreference("fast" | "best"): Route sorting preferenceswapSlippage(number): Default slippage tolerance
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[] }
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)autoRouting(boolean): Auto-routing feature (not implemented)refuel(boolean): Refuel feature (not implemented)
postSwapAction(object): Customize the post-swap screenctaLabel(string): Label for the call-to-action buttonctaAction((hash: string) => Promise<void>): Async action triggered when the CTA button is clickedbannerAddon(ReactNode): Custom React element to render in the post-swap bannerbannerAddonClassNames(string): CSS class names for the banner addon containeronSwapCompleted((hash: string) => void): Callback fired when a swap completes successfullyisPending(boolean): Whether the CTA action is in progress
Base URL and API key / affiliate requirements
The widget supports three backend URLs. Requirements for apiKey and affiliateId depend on which URL you use (or the default when you omit baseUrl):
| Base URL | apiKey | affiliateId |
|----------|--------|-------------|
| Public (default) — https://public-backend.bungee.exchange/api/v1 | Optional | Optional |
| Backend — https://backend.bungee.exchange/api/v1 | Optional | Required |
| Dedicated — https://dedicated-backend.bungee.exchange/api/v1 | Required | Required |
- If you do not pass
baseUrl, the widget uses the public backend. No API key or affiliate ID is required. - For the backend URL, you must provide
affiliateIdin config; the widget will show a validation error otherwise. - For the dedicated URL, you must provide both
apiKeyandaffiliateId; the widget will show a validation error if either is missing. - Using an unsupported or invalid
baseUrlmay result in runtime validation errors or API failures.
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.
Fetching Token Data
When using supportedTokens, you can fetch token data from the Bungee API endpoints:
/tokens/list: Returns a curated list of trending tokens (verified) or the complete token list/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 Bungee API token endpoints 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: number }) => {
console.log("Transaction status:", 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 { Bungee, type BungeeImperativeAPIType } from "@socket.tech/bungee";
export default function App() {
const bungeeRef = useRef<BungeeImperativeAPIType>(null);
const handleSetInflightData = (data: OrderData) => {
bungeeRef.current?.setInflightData(data);
};
return <Bungee config={config} ref={bungeeRef} />;
}Available method:
setInflightData(data: OrderData): Track pending transactions and navigate to inflight screen
Type Exports
import type {
BungeeConfig,
BungeeImperativeAPIType,
WalletAdapter,
TronWebLike,
} from "@socket.tech/bungee";Migration from v0.25.x
What Changed
In v0.25.x (up to v0.25.4), the widget bundled its own wallet provider (wagmi + Reown/WalletConnect). You did not need to pass wallet configuration; the widget managed connections, chain switching, transactions, and contract reads internally.
In v1.0.0, the widget is wallet-provider-agnostic. Your app owns wallet connection state and UI, and Bungee only consumes the minimal adapter you pass through config.wallet.
| | v0.25.x | v1.0.0 |
|---|---|---|
| Wallet provider | Built-in (wagmi + Reown) | Your choice |
| Config | apiKey only | wallet required; apiKey optional for default public backend |
| Wallet connections | Widget handled internally | Host app handles via onOpenWalletConnect |
| Blockchain reads | Widget handled internally | Widget handles internally via viem public clients |
| Blockchain writes | Widget handled internally | Widget handles internally via getEVMWalletClient or getSolanaSigner |
Migration Checklist
- Remove old assumptions that the widget will open or manage wallets for you.
- Add
QueryClientProviderabove<Bungee />. - Build
wallet.accountsfrom your host wallet provider. - Provide
getEVMWalletClientfor EVM support. - Provide
switchChainfor EVM chain switching. Reject or throw if switching fails. - Provide
getSolanaSignerandrpcs.solanafor Solana support. - Implement
eventHandlers.onOpenWalletConnectso the widget can ask your app to open the correct wallet modal.
Troubleshooting
React Query error or
No QueryClient setMountQueryClientProviderabove<Bungee />.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/bungee/styles.cssand@socket.tech/bungee/fonts.css.
Links
- Web App: bungee.exchange
- Bungee Lite Package: @socket.tech/bungee
- Bungee Protocol Docs: docs.bungee.exchange/
