Kevo Docs

Hooks Reference

Complete reference for all React hooks exported by @kevo-ws/sdk/react. All hooks must be used inside a KevoProvider.

All signing hooks follow the same shape: they return { fn, isLoading, error }.error is reset to null on each new invocation. No walletId is needed — the SDK uses the authenticated user's wallet automatically.

useKevoModal

Controls the visibility of the sign-in modal.

typescript
const { open, close, isOpen } = useKevoModal()
PropTypeDescription
open() => voidOpens the sign-in modal.
close() => voidCloses the modal.
isOpenbooleanWhether the modal is currently visible.

useKevoDashboard

Controls the visibility of the settings/dashboard overlay.

typescript
const { open, close, isOpen } = useKevoDashboard()
PropTypeDescription
open() => voidOpens the dashboard overlay.
close() => voidCloses the overlay.
isOpenbooleanWhether the overlay is currently visible.

useKevo

Primary hook for session state, auth methods, and delegation management. Replaces the old useSession / useKevoClient pattern.

typescript
const {
  client,           // KevoClient instance
  session,          // KevoSession | null
  isAuthenticated,  // boolean
  isLoading,        // boolean (true during initial restore)
  userProfile,      // KevoResolvedUserProfile
  rawUserProfile,   // KevoUserProfile | null
  enabledChains,    // SupportedChain[] (e.g. ['evm', 'solana'])
  projectConfig,    // KevoProjectConfig | null
  logout,           // () => Promise<void>
  sendEmailOtp,     // (email: string) => Promise<void>
  verifyEmailOtp,   // (email: string, code: string) => Promise<void>
  loginWithGoogle,  // () => void — opens Google OAuth popup
  loginWithX,       // () => void — opens X (Twitter) OAuth popup
  loginWithApple,   // () => void — opens Apple OAuth popup
  getDelegation,    // () => Promise<KevoDelegation | null>
  grantDelegation,  // (opts?: GrantDelegationOptions) => Promise<KevoDelegation>
  revokeDelegation, // () => Promise<void>
} = useKevo()

useUserProfile

Returns a normalized profile for display plus the raw API profile. Kevo always uses the wallet address as the primary display name, shows provider username/name when available, and falls back to a deterministic generated avatar if the provider does not return one.

typescript
const {
  profile,           // KevoResolvedUserProfile
  rawProfile,        // KevoUserProfile | null
  refresh,           // () => Promise<KevoUserProfile | null>
  requestEmailLink,  // (email: string) => Promise<void>
  confirmEmailLink,  // (email: string, code: string) => Promise<void>
} = useUserProfile()

return (
  <div>
    <img src={profile.avatarUrl} alt="" />
    <span>{profile.name}</span>       {/* wallet address, shortened */}
    {profile.username && <small>{profile.username}</small>}
  </div>
)
PropTypeDescription
profileKevoResolvedUserProfileDisplay-safe profile with name, username, avatarUrl, address, and raw profile.
rawProfileKevoUserProfile | nullRaw user object returned by Kevo, including email and authMethods.
refresh() => Promise<KevoUserProfile | null>Reloads the current user profile from the API.
requestEmailLink(email: string) => Promise<void>Sends an OTP so the signed-in user can link an email.
confirmEmailLink(email: string, code: string) => Promise<void>Verifies the OTP and links the email to the current account.
X does not provide email through its standard profile endpoint. UserequestEmailLink and confirmEmailLink when an email is needed for OTP-gated export.

useWallets

Returns the user's EVM and Solana wallets. The wallet is created automatically after first authentication — no manual creation needed.

typescript
const { evmWallet, solanaWallet, enabledChains, isLoading } = useWallets()

// evmWallet:     KevoWallet | null     — { address: '0x...', id, createdAt }
// solanaWallet:  KevoSolanaWallet | null — { address: 'Base58...', id, createdAt }
PropTypeDescription
evmWalletKevoWallet | nullThe user's embedded EVM wallet, or null if not yet created / chain not enabled.
solanaWalletKevoSolanaWallet | nullThe user's embedded Solana wallet, or null.
enabledChainsSupportedChain[]Project's enabled chains.
isLoadingbooleanTrue while wallets are being loaded.
Single-wallet shortcuts also exist: useWallet() returns { wallet, isLoading } (EVM only) and useSolanaWallet() returns { solanaWallet, isLoading }.

useChain

Manages the active EVM chain when multiple chains are configured via KevoConfig.chains. Switching chain triggers a re-render of all chain-aware hooks (useBalance, useTokenBalance).

typescript
const { activeChainId, setChain, chains, rpcUrl } = useChain()

// Switch to Base
setChain(8453)

// Render a chain selector
Object.keys(chains).map(id => (
  <button key={id} onClick={() => setChain(Number(id))}>
    Chain {id}
  </button>
))
PropTypeDescription
activeChainIdnumber | nullCurrently active EVM chain ID. Null if KevoConfig.chains is not set.
setChain(chainId: number) => voidSwitch the active EVM chain. Throws if chainId is not in KevoConfig.chains.
chainsRecord<number, string>Full chains map from KevoConfig.chains.
rpcUrlstring | nullRPC URL for the currently active chain.
For the full multi-chain setup guide, see the Multi-chain EVM page.

useSignMessage

Chain-aware message signing. Auto-routes to EVM or Solana based on project config. For multi-chain projects, pass { chain: 'solana' } to override.

typescript
const { signMessage, isLoading, error } = useSignMessage()

// Single-chain project — just works:
const sig = await signMessage('Hello from Kevo!')

// Multi-chain project — specify chain:
const sig = await signMessage('Hello', { chain: 'solana' })
PropTypeDescription
signMessage(message: string, options?: { chain?: SupportedChain }) => Promise<string>Signs the message. Returns hex signature.
isLoadingbooleanTrue while signing.
errorError | nullLast error, if any.

useSignSolanaMessage

Explicit Solana message signing — for raw byte signing or fine-grained control.

typescript
const { signSolanaMessage, isLoading, error } = useSignSolanaMessage()

// Accepts string or Uint8Array
const sigHex = await signSolanaMessage('Hello Solana!')
const sigHex = await signSolanaMessage(new Uint8Array([1, 2, 3]))
PropTypeDescription
signSolanaMessage(message: string | Uint8Array) => Promise<string>Signs with Ed25519. Returns 64-byte signature as hex.
isLoadingbooleanTrue while signing.
errorError | nullLast error, if any.

useSignTypedData

Signs EIP-712 structured data with the user's EVM wallet.

typescript
const { signTypedData, isLoading, error } = useSignTypedData()

const sig = await signTypedData({
  domain: { name: 'MyApp', version: '1', chainId: 1 },
  types: { MyType: [{ name: 'value', type: 'uint256' }] },
  primaryType: 'MyType',
  message: { value: 42 },
})
PropTypeDescription
signTypedData(data: Eip712TypedData) => Promise<string>Signs EIP-712 data. Returns hex signature.
isLoadingbooleanTrue while signing.
errorError | nullLast error, if any.

useSignTransaction

Sign and/or broadcast EVM transactions. Returns both signTransaction (sign only) and sendTransaction (sign + auto-fill + broadcast).

sendTransaction automatically fills missing nonce, gasLimit, maxFeePerGas, and maxPriorityFeePerGas from the RPC. You can override any field explicitly.

typescript
const { signTransaction, sendTransaction, isLoading, error } = useSignTransaction()

// Sign only (returns raw signed tx hex):
const rawTx = await signTransaction({ to: '0x...', value: 0n, chainId: 1 })

// Sign + auto-fill + broadcast (returns tx hash):
const txHash = await sendTransaction({
  to: '0xRecipient',
  value: 1_000_000_000_000_000n,
  chainId: 1,
})

// With calldata for smart contract calls:
const txHash = await sendTransaction({
  to: '0xContractAddress',
  data: '0xa9059cbb000000...', // calldata
  chainId: 1,
})

// Override specific fields (nonce, gas, fees):
const txHash = await sendTransaction({
  to: '0xRecipient',
  value: 0n,
  chainId: 1,
  nonce: 42,
  gasLimit: 500_000n,
  maxFeePerGas: '0x3B9ACA00',
})

// Optional rpcUrl override (falls back to evmRpcUrl from config):
const txHash = await sendTransaction(tx, 'https://custom-rpc.com')
PropTypeDescription
signTransaction(tx: TransactionRequest) => Promise<string>Signs without broadcasting. Returns raw signed tx hex.
sendTransaction(tx: TransactionRequest, rpcUrl?: string) => Promise<string>Signs, auto-fills nonce/gas/fees, and broadcasts. Returns tx hash.
isLoadingbooleanTrue while signing/broadcasting.
errorError | nullLast error, if any.

useSignSolanaTransaction

Sign and/or broadcast Solana transactions. sendSolanaTransaction signs, assembles the wire-format transaction, and broadcasts in one call.

typescript
const { signSolanaTransaction, sendSolanaTransaction, isLoading, error } = useSignSolanaTransaction()

// Sign only (returns Ed25519 signature hex):
const sigHex = await signSolanaTransaction(messageBytes)

// Sign + assemble + broadcast (returns base58 tx signature):
const txSig = await sendSolanaTransaction(messageBytes)

// Optional rpcUrl override:
const txSig = await sendSolanaTransaction(messageBytes, 'https://api.devnet.solana.com')
PropTypeDescription
signSolanaTransaction(txBytes: Uint8Array) => Promise<string>Signs the message bytes. Returns Ed25519 signature as hex.
sendSolanaTransaction(txBytes: Uint8Array, rpcUrl?: string) => Promise<string>Signs, assembles, and broadcasts. Returns base58 tx signature.
isLoadingbooleanTrue while signing/broadcasting.
errorError | nullLast error, if any.

useEstimateGas

Estimates gas for an EVM transaction via the configured RPC.

typescript
const { estimateGas, isLoading, error } = useEstimateGas()

const gas: bigint = await estimateGas({
  to: '0xRecipient',
  value: '0x16345785D8A0000',
  chainId: 1,
})
PropTypeDescription
estimateGas(tx: TransactionRequest, rpcUrl?: string) => Promise<bigint>Returns estimated gas units as bigint.
isLoadingbooleanTrue while estimating.
errorError | nullLast error, if any.

useBalance

Fetches and auto-refreshes the native ETH balance. Polls every 15 seconds by default.

typescript
const { balance, isLoading, error, refetch } = useBalance()

// balance: bigint | null (wei), null if not yet fetched
// Example: display in ETH
const ethDisplay = balance !== null
  ? (Number(balance) / 1e18).toFixed(6) + ' ETH'
  : 'Loading…'

// Optional: custom RPC and poll interval
const { balance } = useBalance('https://custom-rpc.com', 30_000)

// Disable auto-polling
const { balance, refetch } = useBalance(undefined, 0)
PropTypeDescription
balancebigint | nullBalance in wei, or null if not yet fetched.
isLoadingbooleanTrue while fetching.
errorError | nullFetch error, if any.
refetch() => Promise<void>Manually trigger a balance refresh.

useTokenBalance

Fetches and auto-refreshes an ERC-20 token balance.

typescript
const { balance, isLoading, error, refetch } = useTokenBalance(
  '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // USDC
)
// balance: bigint | null (smallest unit)
// For USDC (6 decimals): Number(balance) / 1e6
PropTypeDescription
balancebigint | nullToken balance in smallest unit, or null.
isLoadingbooleanTrue while fetching.
errorError | nullFetch error, if any.
refetch() => Promise<void>Manually trigger a refresh.

useSolanaBalance

Fetches and auto-refreshes the native SOL balance.

typescript
const { balance, isLoading, error, refetch } = useSolanaBalance()
// balance: bigint | null (lamports)
// Display in SOL: (Number(balance) / 1e9).toFixed(6) + ' SOL'
PropTypeDescription
balancebigint | nullBalance in lamports, or null.
isLoadingbooleanTrue while fetching.
errorError | nullFetch error, if any.
refetch() => Promise<void>Manually trigger a refresh.

useExportKey / useSolanaExportKey

Two-step private key export. Step 1: request OTP. Step 2: verify OTP and the key is displayed securely inside the Kevo iframe (never returned to your app).

typescript
const { requestExport, confirmExport, isLoading } = useExportKey()
// or for Solana:
const { requestExport, confirmExport, isLoading } = useSolanaExportKey()

// Step 1: returns masked email (e.g. "a***@example.com")
const maskedEmail = await requestExport()

// Step 2: verify OTP — key shown in iframe
await confirmExport('123456')
PropTypeDescription
requestExport() => Promise<string>Sends OTP email. Returns masked email address.
confirmExport(otpCode: string) => Promise<void>Verifies OTP. Key is shown in secure iframe.
isLoadingbooleanTrue during either step.
errorError | nullLast error, if any.
Export requires a verified email on the user account. Users authenticated with X, Apple, passkeys, or external wallets may need to link an email first viauseUserProfile().