Skip to Content
🌐 Frontend SDKHooksExternal Wallet

External Wallet

Connect and interact with external wallets (MetaMask, WalletConnect) alongside embarkAI smart accounts.

embarkAI supports two wallet modes: linked (external wallet controls a smart account) and direct (use the external wallet directly). These hooks let you manage both modes.

Import

import { useWalletMode, useDirectWallet, useSendDirectTransaction, useTransferToLinkedWallet, } from '@embarkai/ui-kit'

useWalletMode

Returns the configured and the active wallet mode. In 'linked' mode, the external wallet is paired with an embarkAI smart account. In 'direct' mode, the external wallet is used as-is without account abstraction. When the Provider is configured with wallet.mode: 'both', the user (or your code) can switch between them.

import { useWalletMode } from '@embarkai/ui-kit' function WalletModeIndicator() { const { activeMode, configMode, isDirect, switchMode } = useWalletMode() return ( <div> <span>{isDirect ? 'Direct Wallet' : 'Smart Account (AA)'}</span> {configMode === 'both' && ( <button onClick={() => switchMode(activeMode === 'direct' ? 'linked' : 'direct')}> Switch mode </button> )} </div> ) }

Return Value

PropertyTypeDescription
configMode'linked' | 'direct' | 'both'Mode from wallet.mode in the Provider config.
activeMode'linked' | 'direct' | 'both'Mode currently in effect.
switchMode(mode: 'linked' | 'direct') => voidSwitch the active mode; meaningful when configMode is 'both'.
isDirectbooleantrue when transactions bypass account abstraction.

useDirectWallet

Access the connected external wallet and its connect/disconnect actions. The hook always returns an object; check isConnected.

import { useDirectWallet } from '@embarkai/ui-kit' function DirectWalletInfo() { const wallet = useDirectWallet() if (!wallet.isConnected) { return <button onClick={() => wallet.connect()}>Connect external wallet</button> } return ( <div> <p>External address: {wallet.address}</p> <p>Connector: {wallet.connectorName}</p> <p>Chain: {wallet.chainId}</p> <button onClick={wallet.disconnect}>Disconnect</button> </div> ) }

Return Value

PropertyTypeDescription
address`0x${string}` | nullThe external wallet address.
chainIdnumber | nullChain the external wallet is on.
isConnectedbooleanWhether an external wallet is connected.
connectorNamestring | nullName of the wallet connector (e.g., 'MetaMask', 'WalletConnect').
connect(flow?) / reconnect(flow?)(flow?: 'link' | 'connect' | 'auth') => voidOpen the wallet connection flow.
disconnect() => voidDisconnect the external wallet.

useSendDirectTransaction

Send a transaction directly from the external wallet, bypassing account abstraction. Useful when the user needs to interact with contracts that don’t support smart accounts.

import { useSendDirectTransaction } from '@embarkai/ui-kit' function DirectSend() { const { sendTransaction, isPending, error, txHash } = useSendDirectTransaction() const handleSend = async () => { await sendTransaction({ to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', value: '0.01', }) } return ( <div> <button onClick={handleSend} disabled={isPending}> {isPending ? 'Sending...' : 'Send (Direct)'} </button> {error && <p className="error">{error}</p>} {txHash && <p>Tx: {txHash}</p>} </div> ) }

Return Value

PropertyTypeDescription
sendTransaction(params: { to: 0x; value: string; data?: 0x; chainId?: number }) => Promise<0x | null>Submit a direct transaction. Returns the tx hash.
isPendingbooleantrue while the transaction is pending.
errorstring | nullError message if the transaction failed.
txHash`0x${string}` | nullThe transaction hash after submission.
reset() => voidClear error and txHash.

useTransferToLinkedWallet

Transfer assets from the smart account to a linked external wallet. Only available in 'linked' mode.

import { useActiveChainId, useTransferToLinkedWallet } from '@embarkai/ui-kit' function FundLinkedWallet() { const chainId = useActiveChainId() const { transfer, isPending, error, userOpHash, linkedWallets } = useTransferToLinkedWallet() const handleTransfer = async () => { await transfer({ token: 'native', amount: '0.5', chainId, }) } if (!linkedWallets.length) return <p>No external wallet linked.</p> return ( <div> <button onClick={handleTransfer} disabled={isPending}> {isPending ? 'Transferring...' : 'Send to linked wallet'} </button> {error && <p className="error">{error}</p>} {userOpHash && <p>Sent! UserOp: {userOpHash}</p>} </div> ) }

Parameters

ParameterTypeRequiredDescription
token`0x${string}` | 'native'YesERC-20 token address, or 'native' for the chain’s native token.
amountstringYesAmount to transfer (human-readable).
chainIdnumberYesChain to transfer on.
decimalsnumberNoToken decimals for ERC-20. Defaults to 18.
toAddress`0x${string}`NoDestination; defaults to the first entry of linkedWallets.

Return Value

PropertyTypeDescription
transfer(params) => Promise<string | null>Execute the transfer. Returns the UserOperation hash.
isPendingbooleantrue while the transfer is pending.
errorstring | nullError message if the transfer failed.
userOpHashstring | nullThe UserOperation hash after submission.
linkedWallets`0x${string}`[]External wallets linked to this account.
reset() => voidClear error and userOpHash.

Full Example

import { useActiveChainId, useWalletMode, useDirectWallet, useTransferToLinkedWallet, } from '@embarkai/ui-kit' function ExternalWalletPanel() { const chainId = useActiveChainId() const { activeMode } = useWalletMode() const wallet = useDirectWallet() const { transfer, isPending } = useTransferToLinkedWallet() if (!wallet.isConnected) return <p>Connect an external wallet to use these features.</p> return ( <div> <p>Mode: {activeMode}</p> <p>External: {wallet.address}</p> {activeMode === 'linked' && ( <button onClick={() => transfer({ token: 'native', amount: '0.1', chainId })} disabled={isPending} > Send 0.1 to linked wallet </button> )} </div> ) }

Next Steps

Last updated on