Documentation Index
Fetch the complete documentation index at: https://docs.li.fi/llms.txt
Use this file to discover all available pages before exploring further.
In Widget Light, the host application owns all wallet connections. The widget running inside the iframe never touches browser extensions or private keys directly. Instead, ecosystem handlers bridge RPC requests from the iframe to your wallet provider.
How Wallet Bridging Works
When the widget inside the iframe needs to perform a wallet operation (send a transaction, sign a message, switch chains), it sends an RPC_REQUEST message via postMessage. The host-side handler processes the request using your wallet provider and sends the result back as an RPC_RESPONSE.
Widget (iframe) Host (your app)
| |
|-- RPC_REQUEST (eth_sendTransaction) ->|
| |-- wagmi sends tx
| |<- tx hash
|<- RPC_RESPONSE (tx hash) ------------|
Wallet state changes (account switches, chain changes, connect/disconnect) are also pushed from the host to the iframe automatically via EVENT messages.
IframeEcosystemHandler Interface
Every ecosystem handler implements the IframeEcosystemHandler interface:
interface IframeEcosystemHandler {
/** Chain type identifier: 'EVM' | 'SVM' | 'UTXO' | 'MVM' | 'TVM' */
chainType: WidgetLightChainType
/** Returns initial wallet state sent with the INIT handshake */
getInitState(): EcosystemInitState | null
/** Handles an RPC request forwarded from the iframe */
handleRequest(id: string, method: string, params?: unknown): Promise<unknown>
/** Subscribes to wallet state changes; returns an unsubscribe function */
subscribe(emit: (event: string, data: unknown) => void): () => void
}
Ecosystem Handlers
EVM — useEthereumIframeHandler()
import { useEthereumIframeHandler } from '@lifi/widget-light/ethereum'
The EVM handler reads wallet state from your wagmi context automatically. No parameters are required.
const ethHandler = useEthereumIframeHandler()
| Detail | Value |
|---|
| Chain type | EVM |
| Reads from | wagmi context (useConnection, useWalletClient, usePublicClient, useSwitchChain) |
| Peer dependencies | wagmi, viem, @wagmi/core |
| Parameters | None |
Supported RPC methods:
| Method | Description |
|---|
eth_accounts | Returns connected accounts |
eth_requestAccounts | Requests account access |
eth_chainId | Returns current chain ID (hex) |
net_version | Returns current network version |
eth_sendTransaction | Sends a transaction (supports EIP-1559 and legacy gas) |
personal_sign | Signs an arbitrary message |
eth_sign | Signs data |
eth_signTypedData_v4 | Signs EIP-712 typed data |
wallet_switchEthereumChain | Switches to a different chain |
wallet_addEthereumChain | Adds a new chain to the wallet |
wallet_sendCalls | Sends batched calls (EIP-5792) |
wallet_getCallsStatus | Gets status of batched calls (EIP-5792) |
wallet_showCallsStatus | Shows batched calls status UI (EIP-5792) |
wallet_getCapabilities | Queries wallet capabilities (EIP-5792) |
Any unrecognized methods are forwarded to the public client (e.g. eth_getBalance, eth_call).
Wallet events emitted: accountsChanged, chainChanged, connect
Solana — useSolanaIframeHandler(params)
import { useSolanaIframeHandler } from '@lifi/widget-light/solana'
The Solana handler is library-agnostic. You pass wallet state explicitly, so it works with any Solana wallet library that provides a wallet-standard Wallet instance.
const solHandler = useSolanaIframeHandler({
address: solanaAddress, // string | null
connected: solanaConnected, // boolean
wallet: solanaWallet, // Wallet from @wallet-standard/base | null
})
| Detail | Value |
|---|
| Chain type | SVM |
| Peer dependencies | @wallet-standard/base |
Parameters:
| Parameter | Type | Description |
|---|
address | string | null | Connected wallet address |
connected | boolean | Whether a wallet is connected |
wallet | Wallet | null | Wallet-standard Wallet instance |
Supported RPC methods:
| Method | Description |
|---|
getAccount | Returns current account address |
signTransaction | Signs a serialized transaction (base64) |
signMessage | Signs an arbitrary message (base64) |
signAndSendTransaction | Signs and sends a transaction (base64) |
Wallet events emitted: accountsChanged, connect, disconnect
Bitcoin — useBitcoinIframeHandler()
import { useBitcoinIframeHandler } from '@lifi/widget-light/bitcoin'
The Bitcoin handler reads wallet state from the @bigmi/react context automatically. No parameters are required.
const btcHandler = useBitcoinIframeHandler()
| Detail | Value |
|---|
| Chain type | UTXO |
| Reads from | @bigmi/react context (useAccount, useConfig) |
| Peer dependencies | @bigmi/client, @bigmi/react |
| Parameters | None |
Supported RPC methods:
| Method | Description |
|---|
getAccount | Returns current account address and public key |
| Other methods | Forwarded to the wallet client via client.request() |
Wallet events emitted: accountsChanged, connect, disconnect
Sui — useSuiIframeHandler()
import { useSuiIframeHandler } from '@lifi/widget-light/sui'
The Sui handler reads wallet state from @mysten/dapp-kit-react hooks automatically. No parameters are required.
const suiHandler = useSuiIframeHandler()
| Detail | Value |
|---|
| Chain type | MVM |
| Reads from | @mysten/dapp-kit-react hooks (useCurrentWallet, useDAppKit, useWalletConnection) |
| Peer dependencies | @mysten/dapp-kit-react |
| Parameters | None |
Supported RPC methods:
| Method | Description |
|---|
getAccount | Returns current account address |
signTransaction | Signs a transaction block (base64) |
signPersonalMessage | Signs an arbitrary message (base64) |
signAndExecuteTransaction | Signs and executes a transaction block |
Wallet events emitted: accountsChanged, connect, disconnect
Tron — useTronIframeHandler(params)
import { useTronIframeHandler } from '@lifi/widget-light/tron'
The Tron handler is library-agnostic. You pass wallet state explicitly, so it works with any Tron wallet library that provides a compatible adapter.
const tronHandler = useTronIframeHandler({
address: tronAddress, // string | null
connected: tronConnected, // boolean
adapter: tronAdapter, // TronAdapter | null
})
| Detail | Value |
|---|
| Chain type | TVM |
| Peer dependencies | None (adapter state is passed explicitly) |
Parameters:
| Parameter | Type | Description |
|---|
address | string | null | Connected wallet address |
connected | boolean | Whether a wallet is connected |
adapter | TronAdapter | null | Wallet adapter with signTransaction and signMessage methods |
Supported RPC methods:
| Method | Description |
|---|
getAccount | Returns current account address |
signTransaction | Signs a TronWeb transaction object (JSON) |
signMessage | Signs an arbitrary message string |
Wallet events emitted: accountsChanged, connect, disconnect
Subpath Imports
Each ecosystem handler is exposed via a subpath import to enable tree-shaking. If you only use EVM, the Solana, Bitcoin, Sui, and Tron handlers (and their peer dependencies) are never included in your bundle:
// Only includes EVM handler code
import { useEthereumIframeHandler } from '@lifi/widget-light/ethereum'
// Only includes Solana handler code
import { useSolanaIframeHandler } from '@lifi/widget-light/solana'
// Only includes Bitcoin handler code
import { useBitcoinIframeHandler } from '@lifi/widget-light/bitcoin'
// Only includes Sui handler code
import { useSuiIframeHandler } from '@lifi/widget-light/sui'
// Only includes Tron handler code
import { useTronIframeHandler } from '@lifi/widget-light/tron'
Combining Multiple Handlers
Pass all your handlers as an array. The widget routes each RPC request to the handler matching the request’s chainType:
import { LiFiWidgetLight } from '@lifi/widget-light'
import { useEthereumIframeHandler } from '@lifi/widget-light/ethereum'
import { useSolanaIframeHandler } from '@lifi/widget-light/solana'
import { useMemo } from 'react'
function App() {
const ethHandler = useEthereumIframeHandler()
const solHandler = useSolanaIframeHandler({
address: solanaAddress,
connected: solanaConnected,
wallet: solanaWallet,
})
const handlers = useMemo(
() => [ethHandler, solHandler],
[ethHandler, solHandler]
)
return (
<LiFiWidgetLight
config={{ integrator: 'my-app' }}
handlers={handlers}
/>
)
}
External Wallet Management
If your app has its own wallet connection UI (a connect button, a modal, etc.), use the onConnect prop to intercept wallet connection requests from the widget. When onConnect is provided, the widget sends a CONNECT_WALLET_REQUEST to the host instead of opening its built-in wallet menu.
import { LiFiWidgetLight } from '@lifi/widget-light'
import type { ConnectWalletArgs } from '@lifi/widget-light'
import { useCallback } from 'react'
function App() {
const handleConnect = useCallback((args?: ConnectWalletArgs) => {
// args.chainId - the chain the widget wants to connect to (optional)
// args.chainType - the chain type ('EVM', 'SVM', 'UTXO', 'MVM', 'TVM') (optional)
openYourWalletModal(args)
}, [])
return (
<LiFiWidgetLight
config={{ integrator: 'my-app' }}
handlers={handlers}
onConnect={handleConnect}
/>
)
}
The ConnectWalletArgs type contains:
| Field | Type | Description |
|---|
chainId | number | undefined | The chain ID the widget wants to connect to |
chainType | WidgetChainType | undefined | The chain type ('EVM', 'SVM', 'UTXO', 'MVM', 'TVM') |
When onConnect is provided, the host automatically sets walletConfig.useExternalWalletManagement: true in the config sent to the iframe. You do not need to set this manually.