This is the full developer documentation for konekt # konekt > A better WalletConnect client for performance-minded apps. A Vite React app first-loads 11.03 kB with Konekt, against 145.74 kB for the official provider and 721.26 kB with AppKit. Konekt gives your app an [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193)-compatible provider backed by WalletConnect v2. It has no `@walletconnect` runtime dependency, and chain support is split into optional imports so unused code can stay out of your bundle. ## A smaller, clearer connection layer [Section titled “A smaller, clearer connection layer”](#a-smaller-clearer-connection-layer) Measured in a real app A Vite React app first-loads **11.03 kB** with Konekt, **145.74 kB** with the official Ethereum Provider, and **721.26 kB** with AppKit. React is marked external in all three. No runtime framework tax Konekt has no `@walletconnect` runtime dependency. Add chain adapters, reads, authentication, and UI only when your app uses them. Your stack stays in charge Keep viem, wagmi, ethers, your RPCs, and your design system. Konekt owns WalletConnect sessions and requests—not the rest of your application. Measured, not guessed See the versioned bundle measurements and full comparison with Reown. [Read the comparison →](./guides/why-konekt/) Start with one provider Call `Provider.init()` once, show the pairing QR, and wait for the user to approve the connection in their wallet. Choose your networks Add EVM, Solana, Bitcoin, or Cosmos networks. Konekt proposes only the networks and wallet methods you configure. Bring your own UI—or use ours Listen for a pairing URI and render it yourself, or install `konekt-ui` for a React wallet picker and QR modal. Add sign-in when you need it The optional SIWE feature requests authentication during pairing. Verify the signed result on your server. ## Where to begin [Section titled “Where to begin”](#where-to-begin) The guides are a path, from the easiest setup to full control. Take them in order: 1. [Getting started](./guides/getting-started/) — a React app with a connect button, using konekt, konekt-ui, and wagmi. 2. [Design your own connect UI](./guides/custom-ui/) — your own buttons and dialogs on top of the pairing hooks. 3. [Plain JavaScript](./guides/vanilla/) — the provider itself, with no framework and no UI package. 4. [Solana](./guides/solana/), [Cosmos](./guides/cosmjs/), [Bitcoin](./guides/bitcoin/), and [Sui](./guides/sui/) — the same provider beyond Ethereum. 5. [Everything together](./guides/multichain/) — one connection covering several ecosystems at once. Or jump straight to what you need: * Want to see it running first? Open the [live showcase](./showcase/): pairing, every EIP-1193 method, kernel reads, and the event log. * Already using `@walletconnect/ethereum-provider`? Follow the [migration guide](./guides/migrate-ethereum-provider/). * Comparing clients? See [why Konekt is better](./guides/why-konekt/) for versioned bundle measurements and architecture trade-offs. * Configuring networks, reads, or request routing? Read [Chains and networks](./guides/chains/). * Using an EVM client library? Follow the [viem](./guides/viem/), [ethers](./guides/ethers/), or [wagmi](./guides/wagmi/) integration guide. * Using Next.js or another server-rendered framework? Read [Frameworks and SSR](./guides/frameworks/). * Configuring storage, timeouts, or diagnostics? Read [Sessions and options](./guides/sessions/). * Keeping the initial page small? See the measured [bundle sizes, tree-shaking, and lazy-loading patterns](./guides/bundle-size/). * Adding Sign-In with Ethereum? Read [Authentication features](./guides/features/). * Something not working? Check [Troubleshooting](./guides/troubleshooting/) for every error Konekt throws. * Looking up a type or function? Open the [API reference](./api/readme/). ## For AI agents [Section titled “For AI agents”](#for-ai-agents) Start with the [integration instructions](./ai/), then use [`/llms.txt`](./llms.txt) for an index or [`/llms-full.txt`](./llms-full.txt) for the complete guides. [llms.txt](./llms.txt)[llms-full.txt](./llms-full.txt) # Getting started > Add a wallet connect button to a React app with konekt, konekt-ui, and wagmi. By the end of this page, your React app has a **Connect wallet** button. Clicking it opens a wallet picker with a QR code, the user approves the connection in a wallet on their phone, and your app can show their address and send transactions. Three packages share the work, and each does one job: | Package | Job | | ----------- | ---------------------------------------------------------- | | `konekt` | Speaks the WalletConnect v2 protocol to the wallet. | | `konekt-ui` | Renders the connect button, wallet picker, and pairing QR. | | `wagmi` | Keeps account, chain, and balance state in React hooks. | This is the smallest amount of code to a working connection, and also the smallest download: this stack first-loads **19.06 kB** in a production Vite app, where AppKit first-loads **721.26 kB**. You do not need to care about that yet—it simply means there is no penalty for starting the easy way. ## Before you start [Section titled “Before you start”](#before-you-start) You need: * a React 18 or 19 app—`pnpm create vite my-app --template react-ts` works; * a free project ID from [WalletConnect Cloud](https://cloud.walletconnect.com/); * a wallet app that supports WalletConnect v2, such as MetaMask, Rainbow, or Trust Wallet, usually on your phone. ## 1. Install [Section titled “1. Install”](#1-install) ```sh pnpm add konekt konekt-ui wagmi viem @tanstack/react-query ``` You can use `npm install` or `yarn add` instead. `konekt-ui` works with React 18 or 19; the wagmi packages can be v2 or v3. The snippets use hook names both wagmi versions export (`useAccount`, `connect`). ## 2. Describe your app and networks [Section titled “2. Describe your app and networks”](#2-describe-your-app-and-networks) Create `src/web3.tsx`. It tells wagmi which networks you support and which wallets can connect—browser extensions through `injected()`, and every WalletConnect wallet through the `konekt` connector: ```tsx import type { PropsWithChildren } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { WagmiProvider, createConfig, http } from "wagmi"; import { mainnet } from "wagmi/chains"; import { injected } from "wagmi/connectors"; import { konekt } from "konekt-ui/wagmi"; export const projectId = "YOUR_PROJECT_ID"; export const config = createConfig({ chains: [mainnet], connectors: [ injected(), konekt({ projectId, metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, }), ], transports: { [mainnet.id]: http(), }, }); // Types chain IDs across wagmi hooks as a union of your configured chains instead of plain `number`. declare module "wagmi" { interface Register { config: typeof config; } } const queryClient = new QueryClient(); export function Web3Provider({ children }: PropsWithChildren) { return ( {children} ); } ``` The `metadata` is what the wallet shows the user when it asks “allow this app to connect?”. To support more networks later, add them to `chains` and `transports`—for example `base` from `wagmi/chains`. ## 3. Wrap your app [Section titled “3. Wrap your app”](#3-wrap-your-app) In `src/main.tsx`, put `Web3Provider` around the app: ```tsx import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "./App"; import { Web3Provider } from "./web3"; createRoot(document.getElementById("root")!).render( , ); ``` ## 4. Add the button [Section titled “4. Add the button”](#4-add-the-button) `ConnectButton` is the complete flow: the trigger, the wallet picker, the QR code, and—once connected—account, network, and disconnect controls. ```tsx import { abortPairing, ConnectButton } from "konekt-ui/wagmi"; import "konekt-ui/styles.css"; export function WalletControls() { return ; } ``` Import the stylesheet once, anywhere in your app. The button finds your project ID through the connector you registered in step 2. `onDismiss={abortPairing}` makes closing the modal also cancel the pending connection. ## 5. Use the connection [Section titled “5. Use the connection”](#5-use-the-connection) Once connected, the wallet behaves like any other wagmi connection. Every wagmi hook works: ```tsx import { formatUnits } from "viem"; import { useAccount, useBalance } from "wagmi"; export function Account() { const account = useAccount(); const balance = useBalance({ address: account.address }); if (!account.isConnected || !account.address) { return

No wallet connected.

; } return (

{account.address}

{balance.data && (

{formatUnits(balance.data.value, balance.data.decimals)} {balance.data.symbol}

)}
); } ``` Sending a transaction is `useSendTransaction()`, switching networks is `useSwitchChain()`—see the [wagmi guide](../wagmi/) for a complete account panel. ## Try it [Section titled “Try it”](#try-it) Run the dev server, click **Connect wallet**, pick a wallet or scan the QR code with your phone, and approve. Three things are worth noticing: * The approved connection is called a **session**. It is saved in the browser, so the user stays connected across page reloads. * The QR code carries a one-time **pairing** secret that introduces your app to the wallet. A new one is created for each attempt. * Signing and transactions are approved in the wallet, not in your app. On mobile, konekt-ui returns the user to their wallet automatically. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) Take these in order—each page assumes the ones before it, and nothing more: 1. [Design your own connect UI](../custom-ui/) — keep your own buttons and dialogs; the hooks do the work. 2. [Plain JavaScript](../vanilla/) — the provider underneath all of this, with no React and no UI package. 3. [Solana](../solana/), [Cosmos](../cosmjs/), [Bitcoin](../bitcoin/), and [Sui](../sui/) — the same provider beyond Ethereum. 4. [Everything together](../multichain/) — one connection covering several ecosystems at once. When something misbehaves, [Troubleshooting](../troubleshooting/) lists every error and its fix. # Design your own connect UI > Keep your own buttons and dialogs by composing the konekt-ui hooks and building blocks, and ship only the pieces you import. [Getting started](../getting-started/) used `ConnectButton`, the fully assembled flow. That component is one arrangement of smaller parts, and every part is exported. This page peels the layers off one at a time: first your own trigger, then your own dialog, then your own wallet list—each step using hooks and small components instead of the prebuilt ones. Nothing here requires new concepts. The hooks return one object, described next, and your components render it. ## The pairing contract [Section titled “The pairing contract”](#the-pairing-contract) `useWagmiPairing()` (for wagmi apps) and `useProviderPairing(provider)` (for apps holding a Konekt `Provider` directly) both return a `Pairing`: | Field | Meaning | | ---------------------- | ---------------------------------------------------------------------------------------- | | `connected` | Whether a wallet is currently connected. | | `local` | Wallets already available in the browser—wagmi connectors, or injected-wallet sources. | | `connectLocal(wallet)` | Connects one of `local`. | | `start(onUri)` | Starts WalletConnect pairing and reports the QR URI. Returns a teardown that cancels it. | | `reset()` | Clears a previous error before a new attempt. | | `error` | A human-readable pairing failure to display. | | `chains` | CAIP-2 chain IDs, used to filter the wallet listings. | | `projectId` | WalletConnect project ID, read from the provider or connector, for Explorer listings. | Everything below consumes this one object, so a component written against it works with wagmi and without it. ## Step 1: your trigger, the ready dialog [Section titled “Step 1: your trigger, the ready dialog”](#step-1-your-trigger-the-ready-dialog) The smallest customization: keep `WalletModal`, replace the button. In a wagmi app: ```tsx import { useState } from "react"; import { WalletModal } from "konekt-ui"; import { abortPairing, useWagmiPairing } from "konekt-ui/wagmi"; import "konekt-ui/styles.css"; export function CustomWalletButton() { const [open, setOpen] = useState(false); const pairing = useWagmiPairing(); return ( <> setOpen(false)} /> ); } ``` Without wagmi, swap the hook: `useProviderPairing(provider)` produces the same `pairing`, and `onDismiss` is unnecessary because the hook owns the cancellation. If the ready dialog is right but the styling is not, you may not need to go further: `theme`, `--kui-*` token overrides in `style`, and the `unstyled` prop restyle `WalletModal` without replacing it. See [theme and custom styles](../konekt-ui/#theme-and-custom-styles). ## Step 2: your dialog, hooks only [Section titled “Step 2: your dialog, hooks only”](#step-2-your-dialog-hooks-only) To own the dialog itself, call `pairing.start()` and render the URI. `QrCode` draws it; `Modal` provides the accessible shell (focus trap, Escape, backdrop, restored focus), or use your design system’s dialog instead: ```tsx import { useEffect, useState } from "react"; import type { Provider } from "konekt"; import { Modal, type Pairing, QrCode, useProviderPairing } from "konekt-ui"; function PairingView({ pairing }: { pairing: Pairing }) { const [uri, setUri] = useState(); // start() subscribes to the URI and begins connecting; its teardown cancels // the attempt, so closing the dialog aborts cleanly. useEffect(() => pairing.start(setUri), [pairing.start]); if (pairing.error) return

{pairing.error}

; if (!uri) return

Preparing the connection…

; return ; } export function ConnectDialog(props: { provider: Provider; open: boolean; onClose: () => void }) { const pairing = useProviderPairing(props.provider); useEffect(() => { if (props.open && pairing.connected) props.onClose(); }, [props.open, pairing.connected, props.onClose]); return ( ); } ``` Two details carry the whole design: * Depend on `pairing.start`, which is stable across renders, rather than on the `pairing` object, which is not. Restarting the effect restarts the pairing. * The QR alone is not enough for someone who cannot scan it. Offer a copy action or a wallet link alongside it, and keep a visible loading state. ## Step 3: your wallet list [Section titled “Step 3: your wallet list”](#step-3-your-wallet-list) `WalletModal` fills its list from two places, and both are available to your components: * `pairing.local` — wallets already in the browser. Render them as buttons that call `pairing.connectLocal(wallet)`. * `fetchWallets()` — one page of WalletConnect Explorer listings, filtered by `filterWallets()` with `include`, `exclude`, and `featured` IDs. On a phone there is nothing to scan, so a tapped wallet should open directly: `walletHref(listing, uri)` builds the deep link from a listing and the pairing URI, `openWalletLink()` navigates to it, and `isMobile()` tells you which presentation to prefer. `walletLink(listing, true)` answers whether a listing can be reached from a phone at all, without needing a URI. Two rules come with that on iOS. Start pairing before the user taps, because WebKit refuses to leave for a custom scheme once the gesture has expired, and call `openWalletLink()` inside the tap handler itself rather than in an effect that waits for the URI. `pairingRefreshDelay(uri)` then tells you how long you may keep offering that URI, so a picker left open replaces a pairing before it lapses instead of offering a link no wallet will accept. `pairingExpiry(uri)` is the raw deadline behind it. A connected account chip is `Avatar` plus `truncateAddress`: ```tsx import { Avatar, truncateAddress } from "konekt-ui"; ``` `Avatar` defaults to 28 CSS pixels, the size of the connect bar chip. Pass `size={64}` for the larger disc in an account dialog. | Export | From | Purpose | | --------------------- | ----------------- | -------------------------------------------------------------------------- | | `Modal` | `konekt-ui` | Accessible dialog shell. | | `QrCode` | `konekt-ui` | Renders a pairing URI. Takes `value` and an optional `size`. | | `Avatar` | `konekt-ui` | Address-derived gradient disc. Takes `address` and an optional `size`. | | `truncateAddress` | `konekt-ui` | Shortens a hex address for a chip or heading. | | `fetchWallets` | `konekt-ui` | Queries the WalletConnect Explorer, one page at a time. | | `filterWallets` | `konekt-ui` | Applies `include`, `exclude`, and `featured` to listings. | | `FEATURED_WALLET_IDS` | `konekt-ui` | The default featured Explorer IDs. | | `walletLink` | `konekt-ui` | The base URL a listing advertised for one platform, or nothing. | | `walletHref` | `konekt-ui` | Wallet deep link from a listing and a pairing URI. | | `openWalletLink` | `konekt-ui` | Navigates to a wallet link. Call it inside the tap that asked for it. | | `isMobile` | `konekt-ui` | Whether to prefer deep links over a QR code. | | `pairingExpiry` | `konekt-ui` | The deadline a pairing URI carries, in unix seconds. | | `pairingRefreshDelay` | `konekt-ui` | How long that URI may still be offered, in milliseconds. | | `AccountModal` | `konekt-ui/wagmi` | The connected account and network dialog, reusable behind a custom button. | ## Keep the bundle honest [Section titled “Keep the bundle honest”](#keep-the-bundle-honest) Building your own UI is also how you keep the download small, if you follow three rules: * **Import only what you render.** `konekt-ui` declares its modules side-effect free, so a production ESM bundler drops every component and helper you never import. A hooks-only dialog does not pay for `WalletModal`, the Explorer client, or the account controls. * **Skip the stylesheet when you own the styles.** `import "konekt-ui/styles.css"` is only for the styled components. Custom components—or `unstyled` ones targeted through their `data-kui` attributes—do not need it. * **Load the dialog when it opens.** Wallet UI is rarely needed on first paint. Put the dialog in its own component and load it with `React.lazy` behind the click; the [bundle size guide](../bundle-size/#lazy-load-the-react-wallet-ui) shows the complete pattern and the measured effect. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) You now control the React layer completely. The next page, [Plain JavaScript](../vanilla/), removes React as well: the provider, its events, and nothing else. That is the layer these hooks are built on, and the one you will use from any framework—or none. # Plain JavaScript > Create a provider, show a pairing QR, and make your first wallet request with no framework and no UI package. The React pages sit on one object: the Konekt `Provider`. This page uses it directly—no React, no wagmi, no konekt-ui. Everything is a method call or an event, so it works in any framework or in none. Three terms appear throughout these docs: * **Provider** — the object your app calls to connect, read account state, and send wallet requests. * **Pairing** — the short-lived QR code or link that introduces the app to a wallet. * **Session** — the connection that remains after the user approves the app. This guide uses Ethereum mainnet, but the same provider can also connect to other EVM networks, Solana, Bitcoin, Cosmos, and custom namespaces. ## Before you start [Section titled “Before you start”](#before-you-start) You need: * a browser application; * a WalletConnect project ID from [WalletConnect Cloud](https://cloud.walletconnect.com/); * a wallet that supports WalletConnect v2. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt ``` You can use `npm install konekt` or `yarn add konekt` instead. The modern-browser EVM path is 14.84 kB minified and gzipped through the first encrypted WalletConnect message. A matched Vite React app first-loads 11.03 kB with Konekt, against 145.74 kB for `@walletconnect/ethereum-provider`. Optional transports, features, chain adapters, and UI use separate entry points. See [Why Konekt is better](../why-konekt/) for the comparison and [Bundle size and loading](../bundle-size/) for complete measurements and on-demand initialization. ## 1. Create the provider [Section titled “1. Create the provider”](#1-create-the-provider) Import `Provider` from the main package and the EVM chain helper from `konekt/eip155`: ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ethereumMainnet], }); ``` `ethereumMainnet` is the ready-made Ethereum chain; the `evm()` factory builds any other EVM network from its chain ID. Do not pass a bare number to `chains`. `Provider.init()` creates one shared provider for the current JavaScript runtime and restores a saved session when possible. Call it once during app setup. Later calls return the same provider and do not apply new options. ## 2. Show the pairing URI [Section titled “2. Show the pairing URI”](#2-show-the-pairing-uri) Register the listener before calling `connect()`: ```ts const showPairingUri = (uri: string) => { // Encode `uri` as a QR code and render it. }; provider.on("display_uri", showPairingUri); try { if (!provider.connected) { await provider.connect(); } } finally { provider.off("display_uri", showPairingUri); } ``` `connect()` waits until the user approves or rejects the proposal. The `display_uri` event arrives while it is waiting. Any QR library can render the URI—it is an ordinary string. In React, [konekt-ui](../konekt-ui/) provides a complete modal, and its `QrCode` component alone renders the URI if that is all you need. Pass an `AbortSignal` when your UI has a Cancel or Close button: ```ts const controller = new AbortController(); const connecting = provider.connect({ signal: controller.signal }); function closePairingUi() { controller.abort(); } const session = await connecting; ``` Do not log or permanently store the pairing URI. Treat it as a temporary connection secret. ## 3. Read the connected account [Section titled “3. Read the connected account”](#3-read-the-connected-account) After the session connects, the EVM adapter adds `accounts` and `chainId` to the provider: ```ts console.log(provider.accounts); // ["0x…"] console.log(provider.chainId); // 1 ``` These properties exist only when you configure at least one EVM chain. For an app with several chain namespaces, `provider.accountsByChain` groups every approved address by its [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) chain ID: ```ts console.log(provider.accountsByChain); // { "eip155:1": ["0x…"] } ``` ## 4. Send a wallet request [Section titled “4. Send a wallet request”](#4-send-a-wallet-request) ```ts const signature = await provider.request({ method: "personal_sign", params: ["0x48656c6c6f", provider.accounts[0]], }); ``` Signing and transaction methods go to the wallet. Read-only JSON-RPC methods such as `eth_getBalance` need an HTTP transport configured for that chain. See [Chains and networks](../chains/) for the distinction. On mobile, the user must return to their wallet to approve. Listen for `request_sent` and open the wallet’s URL; [Wallet UI](../wallet-ui/) covers this event and the rest of the event surface. ## Use an EVM client library [Section titled “Use an EVM client library”](#use-an-evm-client-library) * [viem](../viem/) can wrap the provider with `custom()` for typed wallet actions and reads. * [ethers](../ethers/) can wrap the provider with `BrowserProvider` for Ethers v6 signers and reads. * [wagmi](../wagmi/) connects React state and hooks through the `konekt-ui/wagmi` connector. * [Solana](../solana/) and [CosmJS](../cosmjs/) use small application-owned bridges over namespace requests. ## Disconnect [Section titled “Disconnect”](#disconnect) ```ts await provider.disconnect(); ``` This ends the session and emits `disconnect`. The user will need to pair again before another wallet request. ## Common errors [Section titled “Common errors”](#common-errors) Konekt throws `ProviderRpcError` for provider and JSON-RPC failures: | Code | Meaning | What to do | | -------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | | `4100` | There is no connected session. | Call and await `connect()` first. | | `4200` | The method is unsupported, the wallet declined to approve it, or an EVM read has no transport. | Read the message: it names the method and, for a declined method, lists what the wallet did approve. | | `-32602` | The request parameters are malformed, or the targeted chain is not configured. | Check the method’s expected `params`, and add the chain to `chains` before targeting it. | User rejection and wallet errors can have other codes. Show the message to the user when it is useful, but do not assume every error is a Konekt error. [Troubleshooting](../troubleshooting/) lists the errors Konekt throws as plain `Error` values, such as an expired proposal or a rejected relay connection. ## Creating isolated providers in tests [Section titled “Creating isolated providers in tests”](#creating-isolated-providers-in-tests) ```ts const testProvider = await Provider.create( { projectId: "test", metadata, chains: [ethereumMainnet] }, { session: fakeSession }, ); ``` `Provider.create()` returns a new instance every time. It is intended for tests that need to inject a relay, session, seed, or storage. When you inject `session`, Konekt does not open a real relay connection. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) * [Chains and networks](../chains/) — more EVM networks, JSON-RPC reads, and request routing. * [Sessions and options](../sessions/) — storage, expiry, timeouts, and the rest of `Provider.init()`. * [Wallet UI](../wallet-ui/) — the full event surface for building your own connection flow. * [Solana](../solana/), [Cosmos](../cosmjs/), [Bitcoin](../bitcoin/), and [Sui](../sui/) — the same provider beyond Ethereum. # Sui and custom namespaces > Connect Sui wallets with the generic forwarding adapter, and use the same recipe for any WalletConnect namespace Konekt does not ship. Konekt ships adapters for EVM, Solana, Bitcoin, and Cosmos. Every other WalletConnect namespace follows one pattern: the app declares which methods exist, and each request is forwarded to the wallet. `forwardingNamespace()` from `konekt/generic` builds such an adapter in your application—no Konekt change, no extra package. This page uses Sui as the worked example, then gives the general recipe. ## Build the Sui chain [Section titled “Build the Sui chain”](#build-the-sui-chain) The [WalletConnect Sui namespace](https://docs.reown.com/advanced/multichain/rpc-reference/sui-rpc) defines the chains `sui:mainnet`, `sui:testnet`, and `sui:devnet`, and four methods. Declare them once, in a file such as `src/chains/sui.ts`: ```ts import { forwardingNamespace } from "konekt/generic"; const { chain: sui } = forwardingNamespace({ namespace: "sui", methods: [ "sui_getAccounts", "sui_signPersonalMessage", "sui_signTransaction", "sui_signAndExecuteTransaction", ], }); export const suiMainnet = sui("mainnet"); ``` Each `sui(reference)` call creates one chain. `sui("testnet")` and `sui("devnet")` work the same way. The Sui RPC standard is still under review Reown marks these method signatures as subject to change, and wallet support varies. Confirm pairing and each method with the wallets you intend to support. ## Connect [Section titled “Connect”](#connect) A custom chain configures the provider exactly like a shipped one: ```ts import { Provider } from "konekt"; import { suiMainnet } from "./chains/sui"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [suiMainnet], }); // Render this as a QR code. See the Plain JavaScript guide. const showPairingUri = (uri: string) => console.log(uri); provider.on("display_uri", showPairingUri); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) await provider.connect(); ``` The connection lifecycle—pairing, cancellation, restored sessions—is the same as everywhere else; [Plain JavaScript](../vanilla/) walks through it. ## Read the approved address [Section titled “Read the approved address”](#read-the-approved-address) Approved addresses are grouped by CAIP-2 ID. A wallet can approve a session without a Sui account, so check before using one: ```ts const [address] = provider.accountsByChain[suiMainnet.id] ?? []; if (!address) throw new Error("The wallet approved no Sui account"); ``` Session accounts are addresses only. When you also need public keys, ask the wallet: ```ts const accounts = await provider.request({ method: "sui_getAccounts", params: {} }); // [{ pubkey: "…", address: "0x…" }] per the Sui RPC reference ``` ## Sign [Section titled “Sign”](#sign) Forwarded methods take the parameters the namespace specification defines, and the wallet’s result comes back as `unknown`—parse it before use: ```ts const result = await provider.request({ method: "sui_signPersonalMessage", params: { message: "Sign in to My app", address }, }); if (typeof result !== "object" || result === null || !("signature" in result)) { throw new Error("The wallet returned an unexpected signPersonalMessage result"); } ``` Transactions are built with your Sui SDK (such as `@mysten/sui`), serialized to base64-encoded BCS bytes, and sent as `transaction` alongside the sender’s `address`: ```ts declare const transactionBase64: string; // base64 BCS bytes from your Sui SDK const executed = await provider.request({ method: "sui_signAndExecuteTransaction", params: { transaction: transactionBase64, address }, }); // { digest: "…" } — look the transaction up in an explorer ``` `sui_signTransaction` signs without executing and returns `signature` and `transactionBytes`. Requesting a method the wallet declined during approval fails locally with `4200`, and the message lists what it did approve. ## The recipe for any namespace [Section titled “The recipe for any namespace”](#the-recipe-for-any-namespace) Sui needed nothing Sui-specific: a namespace name, a method list, and a chain reference. Any forwarding-only namespace works the same way: ```ts import { forwardingNamespace } from "konekt/generic"; const { chain } = forwardingNamespace({ namespace: "example", methods: ["example_signMessage"], events: ["example_accountsChanged"], }); const exampleMainnet = chain("mainnet"); ``` Declared methods go to the wallet on the targeted, active, or first configured chain in the namespace. Declared session events surface as the provider’s `message` event: ```ts provider.on("message", ({ type, data }) => { if (type === "example_accountsChanged") refreshAddresses(data); }); ``` Konekt’s own Solana, Bitcoin, and Cosmos adapters are this same forwarding adapter with their method lists filled in; only EVM is different, because it has local answers and a read transport. See [Chains and networks](../chains/) for how requests are routed. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) [Everything together](../multichain/) combines Sui with EVM, Solana, and Cosmos in one provider and one wallet modal. # Everything together > One provider, one wallet modal, and one session covering EVM, Solana, Cosmos, Bitcoin, and Sui. Each previous page connected one ecosystem. A Konekt provider is not limited to one: give it every chain your app supports, and it makes a single WalletConnect proposal covering all of them. The user approves once, and the wallet grants what it can. This page assembles the pieces: one provider, one modal that also lists injected Solana and Cosmos extensions, and the routing rules for using each ecosystem afterwards. ## One provider, every network [Section titled “One provider, every network”](#one-provider-every-network) ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; import { http } from "konekt/http"; import { solanaMainnet } from "konekt/solana"; import { cosmoshub } from "konekt/cosmos"; import { bitcoinMainnet } from "konekt/bip122"; import { suiMainnet } from "./chains/sui"; // from the Sui guide export const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ evm(1, { read: http("https://ethereum.example-rpc.com") }), solanaMainnet, cosmoshub, bitcoinMainnet, suiMainnet, ], }); ``` Each adapter comes from its own entry point, so an app that drops a namespace later also drops its code. Only the EVM chain takes a `read` transport; the other namespaces send every method to the wallet. `Provider.init()` is a process singleton and the first call fixes the options, so this one call must list every chain and feature the app can ever use. Use the provider path, not the wagmi connector The `konekt-ui/wagmi` connector builds its provider from the wagmi config, which only describes EVM chains. A multi-ecosystem app should own `Provider.init()` as above and drive the UI with `useProviderPairing`. Wagmi can still wrap the same connection for its EVM hooks, but the provider configuration must not be delegated to it. ## One modal [Section titled “One modal”](#one-modal) `useProviderPairing` drives `WalletModal` from the provider. `sources` add injected extensions—Phantom-style Solana wallets through Wallet Standard, and Keplr-style Cosmos wallets—as installed choices next to WalletConnect pairing: ```tsx import { useState } from "react"; import type { Provider } from "konekt"; import { useProviderPairing, WalletModal } from "konekt-ui"; import { type CosmosInjectedWallet, useCosmosSource } from "konekt-ui/cosmos"; import { useWalletStandardSource, type WalletStandardWallet } from "konekt-ui/wallet-standard"; import "konekt-ui/styles.css"; export function MultiChainConnection({ provider }: { provider: Provider }) { const [open, setOpen] = useState(false); const [solanaWallet, setSolanaWallet] = useState(); const [cosmosWallet, setCosmosWallet] = useState(); const solana = useWalletStandardSource({ onConnect: setSolanaWallet }); const cosmos = useCosmosSource({ chainIds: ["cosmoshub-4"], onConnect: setCosmosWallet }); const pairing = useProviderPairing(provider, { sources: [solana, cosmos] }); return ( <> setOpen(false)} /> ); } ``` An injected wallet connects outside the session: after `onConnect`, the app signs with that wallet’s own API, and the Konekt provider is not involved. The bridges and request patterns below apply to accounts approved over the WalletConnect session. ## What the wallet actually granted [Section titled “What the wallet actually granted”](#what-the-wallet-actually-granted) `chains` is a proposal, not a guarantee. A wallet may approve some namespaces and skip others, so read the result instead of assuming it: ```ts console.log(provider.accountsByChain); // { // "eip155:1": ["0x…"], // "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": ["…"], // "cosmos:cosmoshub-4": ["cosmos1…"], // } const [solanaAddress] = provider.accountsByChain[solanaMainnet.id] ?? []; ``` Requesting a method the wallet declined fails locally with `4200`, and the message lists what it did approve. Build the UI from `accountsByChain`: show each ecosystem’s features only when the wallet granted an account for it. ## Route each ecosystem’s work [Section titled “Route each ecosystem’s work”](#route-each-ecosystems-work) The session is shared; the client libraries on top stay per-ecosystem, exactly as in their individual guides: | Ecosystem | On top of the provider | Guide | | --------- | --------------------------------------------------------------------- | -------------------------------------- | | EVM | viem `custom(provider)`, ethers `BrowserProvider`, or raw `request()` | [viem](../viem/), [ethers](../ethers/) | | Solana | The application-owned web3.js or Kit bridge | [Solana](../solana/) | | Cosmos | The application-owned CosmJS signer factories | [Cosmos](../cosmjs/) | | Bitcoin | Raw `request()` with parsed results | [Bitcoin](../bitcoin/) | | Sui | Raw `request()` with parsed results | [Sui](../sui/) | Every namespace has an active chain—initially the first one you configured for it—and a request targets its namespace’s active chain by default. To aim one request elsewhere, pass a CAIP-2 ID as the second argument; the chain must be in the `chains` configuration: ```ts const balance = await provider.request( { method: "eth_getBalance", params: [account, "latest"] }, "eip155:1", ); ``` The Solana and Cosmos bridges take a `chainId` when you create them, so one provider serves mainnet and devnet wallets side by side. [Chains and networks](../chains/#targeting-a-chain) covers the selection rules. ## Add sign-in [Section titled “Add sign-in”](#add-sign-in) Authentication rides the same single approval. Add `siwe()` to `features` in the `Provider.init()` call and the wallet signs in while it approves the session—one flow, no second prompt. The server then verifies the result with `konekt/cacao`. [Authentication](../features/) covers both halves. ## Keep it light [Section titled “Keep it light”](#keep-it-light) A five-namespace app does not need to ship five namespaces to every visitor: * Each adapter, the read transport, SIWE, and the UI are separate imports; nothing above pulled in code for a namespace it does not use. * The whole wallet stack can load on demand—`Provider.init()` behind the connect click, the modal behind `React.lazy`. The [bundle size guide](../bundle-size/) shows the measured patterns. * Keep `konekt/cacao` on the server. Browser code never verifies signatures. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) You have seen the whole library. What remains is reference: * [Chains and networks](../chains/) — routing rules, named chains, read transports. * [Sessions and options](../sessions/) — persistence, expiry, timeouts, diagnostics. * [Frameworks and SSR](../frameworks/) — Next.js and other server-rendered setups. * [Troubleshooting](../troubleshooting/) — every error, with fixes. # Why Konekt is better > A smaller, modular, and more explicit WalletConnect client than the official provider or a full AppKit integration. Konekt is the better WalletConnect client for apps that value performance, control, and a small dependency surface. It implements the WalletConnect v2 protocol directly, exposes a focused EIP-1193 provider, and keeps chains, authentication, reads, and UI behind separate imports. WalletConnect is still the network Konekt does not replace the WalletConnect protocol or relay network. It is an alternative browser client implementation and still uses a WalletConnect project ID. Reown builds the official SDKs and AppKit. ## Better at the connection layer [Section titled “Better at the connection layer”](#better-at-the-connection-layer) Konekt improves the parts of the official client stack that make a wallet connection unnecessarily heavy or opaque: * **A real React app first-loads 11.03 kB with Konekt, against 145.74 kB for the official Ethereum Provider and 721.26 kB for AppKit.** Those are production Vite builds of matched apps in this repository, with React marked external. * **No `@walletconnect` runtime dependency.** Konekt owns a compact implementation instead of layering an EVM provider over Universal Provider, Sign Client, Core, utilities, storage, and UI packages. * **Pay only for what you import.** Chain adapters, HTTP reads, SIWE, server verification, and React UI are separate public entry points. * **Use the platform before a polyfill.** Web Crypto handles secure curves, hashing, and key derivation on modern browsers; compatibility implementations load only when an operation is unavailable. * **Explicit behavior instead of hidden policy.** Configured chain objects declare methods, events, and read transports. Wallet requests and HTTP reads have a visible routing boundary. * **Your application stack stays in charge.** Konekt works under viem, wagmi, or ethers instead of trying to become the application’s account, token, and transaction framework. * **Authentication has the right trust boundary.** The browser requests SIWE; a separate server import verifies the CACAO signature and claims. Reown AppKit remains a different choice: it is a complete onboarding product with email and social login, embedded wallets, smart accounts, swaps, on-ramp, payments, and a large prebuilt UI. Choose [AppKit](https://docs.reown.com/appkit/overview) when you need that product suite. Choose Konekt when you need wallet connectivity without making that suite your application architecture. ## Bundle comparison [Section titled “Bundle comparison”](#bundle-comparison) Package-main-bundle numbers understate the official stack. A production Vite React app that only connects Ethereum and shows an address transfers **11.03 kB** on first load with Konekt, **145.74 kB** with `@walletconnect/ethereum-provider@2.23.10`, **19.06 kB** with Konekt UI, and **721.26 kB** with `@reown/appkit@1.8.23`. After every lazy chunk is counted, those become **33.76 kB**, **538.06 kB**, **45.52 kB**, and **1079.28 kB**. React is marked external in all four builds. | Vite app | First load | Overall | | ------------------------------------------ | ------------- | -------------- | | Konekt | **11.03 kB** | **33.76 kB** | | `@walletconnect/ethereum-provider@2.23.10` | **145.74 kB** | **538.06 kB** | | Konekt + `konekt-ui` | **19.06 kB** | **45.52 kB** | | `@reown/appkit@1.8.23` + ethers adapter | **721.26 kB** | **1079.28 kB** | Headless Konekt is **92.4%** smaller on first load and **93.7%** smaller overall. With a wallet modal, Konekt is **97.4%** smaller on first load and **95.8%** smaller overall than AppKit. The four apps live in `packages/size-walletconnect`, `packages/size-appkit`, `packages/size-konekt`, and `packages/size-konekt-ui`. They share React 19 and Vite; the AppKit app turns email, socials, swaps, on-ramp, and analytics off. The library-only path is still **14.84 kB** through the first encrypted message (10.00 kB initially and a 4.84 kB lazy cipher chunk) plus **13.28 kB** for the wallet modal and styles, for a **28.11 kB** connect stack before React. That is the figure `pnpm size` enforces. The Vite table is what a browser actually downloads. The [bundle size guide](../bundle-size/) documents both measurements. The [Konekt UI guide](../konekt-ui/) compares the UI packages directly, including wallet selection, pairing, account controls, theming, and the larger AppKit features Konekt intentionally leaves to the application. ## Why the implementation stays small [Section titled “Why the implementation stays small”](#why-the-implementation-stays-small) ### Native cryptography first [Section titled “Native cryptography first”](#native-cryptography-first) Konekt asks Web Crypto to perform Ed25519, X25519, SHA-256, and HKDF operations. Noble implementations are dynamic compatibility chunks rather than part of the normal modern-browser path. ChaCha20-Poly1305 remains a lazy JavaScript chunk because browsers do not expose that WalletConnect cipher through Web Crypto. ### Narrow public entry points [Section titled “Narrow public entry points”](#narrow-public-entry-points) The provider does not import chain adapters, HTTP reads, SIWE, CACAO verification, or React UI. Your imports describe the code you ship: ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; ``` Add `konekt/http`, `konekt/siwe`, another chain adapter, or `konekt-ui` only when the application uses it. ### Wallet connectivity, not an application framework [Section titled “Wallet connectivity, not an application framework”](#wallet-connectivity-not-an-application-framework) Konekt owns pairing, sessions, encrypted relay messages, and wallet requests. It does not own balances, token discovery, swaps, on-ramp, embedded accounts, or application state. That boundary avoids shipping a second copy of capabilities your viem, wagmi, ethers, or product code already provides. ### Explicit chain and request routing [Section titled “Explicit chain and request routing”](#explicit-chain-and-request-routing) Each configured chain declares its namespace, methods, events, and optional read transport. Wallet methods go to the approved wallet; configured JSON-RPC reads can go to your HTTP transport. Unsupported requests fail instead of silently selecting a broad default. ## A focused product is the advantage [Section titled “A focused product is the advantage”](#a-focused-product-is-the-advantage) Konekt deliberately leaves wallet ranking, modal design, public RPC selection, token features, and product analytics under application control. That is not a missing abstraction for teams already using viem, wagmi, ethers, or their own design system—it prevents the connection library from taking over responsibilities the app already owns. If your requirement is “connect to WalletConnect wallets and make requests,” Konekt is the smaller, clearer, and more composable choice. # Chains and networks > Configure EVM, Solana, Bitcoin, Cosmos, or a custom WalletConnect namespace. Konekt includes only the chain adapters you import. Each adapter describes a WalletConnect **namespace**—a family of networks with the same methods, such as EVM (`eip155`) or Solana. ## Chain IDs [Section titled “Chain IDs”](#chain-ids) WalletConnect identifies a network with a [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) string in the form `namespace:reference`. Each adapter exports ready-made chains for its common networks: | Network | Konekt configuration | CAIP-2 ID | | ---------------- | -------------------- | ----------------------------------------- | | Ethereum mainnet | `ethereumMainnet` | `eip155:1` | | Base | `baseMainnet` | `eip155:8453` | | Solana mainnet | `solanaMainnet` | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | | Bitcoin mainnet | `bitcoinMainnet` | `bip122:000000000019d6689c085ae165831e93` | | Cosmos Hub | `cosmoshub` | `cosmos:cosmoshub-4` | EVM chains can also be built from the ordinary decimal chain ID: `evm(1)` creates the same chain as `ethereumMainnet`. The other namespaces use string references defined by their CAIP standards. ## Configure the provider [Section titled “Configure the provider”](#configure-the-provider) Give `Provider.init()` one or more of those `Chain` objects: ```ts import { Provider } from "konekt"; import { baseMainnet, ethereumMainnet } from "konekt/eip155"; import { solanaMainnet } from "konekt/solana"; import { bitcoinMainnet } from "konekt/bip122"; import { cosmoshub } from "konekt/cosmos"; const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet, baseMainnet, solanaMainnet, bitcoinMainnet, cosmoshub], }); ``` Each factory call creates one chain; named exports such as `solanaMainnet` are ready-made chains. Mix them freely in the array. Do not write `chains: [1, 8453]`. Numeric IDs are accepted only as arguments to `evm()`. A single named chain still needs an array, because `chains` always takes a list: ```ts const provider = await Provider.init({ projectId, metadata, chains: [solanaMainnet] }); ``` ## EVM networks [Section titled “EVM networks”](#evm-networks) Named exports (listed below) cover the common networks. `evm()` builds any other EVM chain from its decimal chain ID: ```ts import { evm } from "konekt/eip155"; const zksync = evm(324); ``` The EVM adapter routes each method one of four ways: | Outcome | Methods | | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Answered locally from session state | `eth_chainId` always; `eth_accounts` and `eth_requestAccounts` once a session exists; `wallet_switchEthereumChain` when the wallet already approved the requested chain | | Sent to the wallet | Signing and transaction methods, plus `wallet_switchEthereumChain` for a chain the session does not yet include | | Sent to the chain’s `read` transport | The remaining `eth_*`, `net_*`, and `web3_*` methods | | Rejected without reaching the wallet | Everything else | Two rejections are worth knowing before you debug them: * Account and wallet methods throw `4100` when there is no session yet. Await `connect()` first. * A method the wallet declined during approval throws `4200` locally rather than producing an opaque wallet error. The message lists what the wallet did approve. ### Add JSON-RPC reads [Section titled “Add JSON-RPC reads”](#add-json-rpc-reads) `http()` creates a JSON-RPC transport for read-only calls: ```ts import { Provider } from "konekt"; import { http } from "konekt/http"; import { evm } from "konekt/eip155"; const ethereum = evm(1, { read: http("https://ethereum.example-rpc.com"), }); const base = evm(8453, { read: http("https://base.example-rpc.com"), }); const provider = await Provider.init({ projectId, metadata, chains: [ethereum, base], }); ``` Use a transport connected to the same network as the chain. One `evm()` call creates one chain, so networks with different RPC URLs are separate calls, as above. Reading from an EVM chain you configured without a `read` transport fails with error `4200` rather than borrowing another chain’s transport. The read transport is not a fallback for arbitrary methods. For example, `personal_sign` always goes to the wallet, while an unknown method still fails with error `4200`. ### Named chains [Section titled “Named chains”](#named-chains) `konekt/eip155` exports the most common networks and their canonical testnets: | Export | CAIP-2 ID | | ----------------- | ----------------- | | `ethereumMainnet` | `eip155:1` | | `ethereumSepolia` | `eip155:11155111` | | `baseMainnet` | `eip155:8453` | | `baseSepolia` | `eip155:84532` | | `bscMainnet` | `eip155:56` | | `bscTestnet` | `eip155:97` | | `arbitrumMainnet` | `eip155:42161` | | `arbitrumSepolia` | `eip155:421614` | | `optimismMainnet` | `eip155:10` | | `optimismSepolia` | `eip155:11155420` | | `polygonMainnet` | `eip155:137` | | `polygonAmoy` | `eip155:80002` | Named chains carry no read transport. For JSON-RPC reads, build the chain with `evm()` and a `read`, or pass a chain definition as below. ### viem, wagmi, and AppKit definitions [Section titled “viem, wagmi, and AppKit definitions”](#viem-wagmi-and-appkit-definitions) `evm()` accepts chain definitions from viem, wagmi, or AppKit directly. The definition’s first default HTTP RPC URL becomes that chain’s read transport, so reads work with no extra configuration: ```ts import { evm } from "konekt/eip155"; import { base, mainnet } from "viem/chains"; chains: [evm(mainnet), evm(base)]; ``` With wagmi, pass the config’s chains unchanged: ```ts chains: config.chains.map((c) => evm(c)); ``` An explicit `read` overrides the definition’s URL, as in `evm(mainnet, { read: http(myRpcUrl) })`. Bare numeric IDs never get an implicit transport. For a network outside the named set, import its definition from `viem/chains` and pass it to `evm()` the same way. Configure `read` on every EVM chain you read from A JSON-RPC read uses the active chain’s transport and fails with `4200` when that chain has none. A wallet may also switch to a chain outside your `chains` configuration: Konekt forwards its `chainChanged`, but the active chain stays one you configured, so no read silently answers from another network. Configure every chain your app supports, and treat `chainChanged` for an unknown chain as an unsupported-network state in your UI. After you configure EVM, the provider has two additional properties: * `provider.chainId` — the active decimal EVM chain ID, always one of the chains you configured; * `provider.accounts` — the unique EVM addresses the wallet approved on those chains. ## Other namespaces [Section titled “Other namespaces”](#other-namespaces) Solana, Bitcoin, and Cosmos send every supported request to the wallet. They do not have built-in HTTP reads. | Import | Ready-made chains | Build other chains | | --------------- | --------------------------------------------------- | -------------------- | | `konekt/solana` | `solanaMainnet`, `solanaDevnet`, `solanaTestnet` | `solana(reference)` | | `konekt/bip122` | `bitcoinMainnet`, `bitcoinTestnet`, `bitcoinSignet` | `bitcoin(reference)` | | `konekt/cosmos` | `cosmoshub`, `osmosis` | `cosmos(reference)` | The `reference` is the part after the colon in a CAIP-2 ID. Each factory also accepts a network definition with a string `id`, such as AppKit’s Solana and Bitcoin networks. For example: ```ts import { cosmos } from "konekt/cosmos"; const myCosmosNetwork = cosmos("my-chain-1"); // id: "cosmos:my-chain-1" ``` ## Targeting a chain [Section titled “Targeting a chain”](#targeting-a-chain) By default, a request uses the active chain in its namespace. Each namespace starts with the first chain you configured for it as the active one, so there is always an active chain. Pass a CAIP-2 ID as the second argument to target one request: ```ts const balance = await provider.request( { method: "eth_getBalance", params: [account, "latest"] }, "eip155:8453", ); ``` This does not change the active chain. The target must already be present in the provider’s `chains` configuration; targeting anything else throws `-32602` with a message naming the missing chain. To ask an EVM wallet to switch its active chain, send the standard wallet method: ```ts await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: "0x2105" }], // Base, decimal 8453 }); ``` ## Build a custom namespace [Section titled “Build a custom namespace”](#build-a-custom-namespace) Use `forwardingNamespace()` when a WalletConnect namespace only needs to forward a known list of methods and events: ```ts import { forwardingNamespace } from "konekt/generic"; const { chain: myChain } = forwardingNamespace({ namespace: "example", methods: ["example_signMessage"], events: ["example_accountsChanged"], }); const example = myChain("mainnet"); ``` Declared methods go to the wallet. Declared events appear as the provider’s `message` event: ```ts provider.on("message", ({ type, data }) => { console.log(type, data); }); ``` Import adapters from their subpaths rather than from `konekt`. This keeps the core package independent of chain-specific code and lets your bundler omit adapters you do not use. Client libraries such as [viem](../viem/), [ethers](../ethers/), [Solana web3.js and Kit](../solana/), and [CosmJS](../cosmjs/) sit on top of these adapters. They are not extra Konekt packages. # Sessions and provider options > Configure storage, the relay URL, protocol lifetimes, and diagnostics, and understand how a session is restored, expires, and ends. [Plain JavaScript](../vanilla/) uses the three required options. This guide covers the rest of `Provider.init()` and what happens to a session between page loads. ## All provider options [Section titled “All provider options”](#all-provider-options) | Option | Required | Default | Purpose | | ----------- | -------- | --------------------------------------------- | -------------------------------------------------------------------------------- | | `projectId` | Yes | — | Authenticates your app to the WalletConnect relay. | | `metadata` | Yes | — | Name, description, URL, and icons the wallet shows during approval. | | `chains` | Yes | — | `Chain` objects from adapters. See [Chains and networks](../chains/). | | `features` | No | none | Proposal hooks such as `siwe()`. See [Authentication](../features/). | | `relayUrl` | No | `wss://relay.walletconnect.org` | Alternative relay WebSocket URL. | | `storage` | No | `localStorage` in a browser | Where the relay identity and session are persisted. `null` disables persistence. | | `ttl` | No | See [Protocol lifetimes](#protocol-lifetimes) | Overrides individual protocol timeouts, in seconds. | | `onDebug` | No | none | Receives structured protocol diagnostics. | `Provider.init()` is a process singleton. The first call fixes every option above; later calls return the same provider and ignore new options. Add every chain and feature your app can ever need to that first call. ## What is stored, and where [Section titled “What is stored, and where”](#what-is-stored-and-where) Konekt persists three keys so a session survives a page reload: | Key | Contents | | ---------------- | -------------------------------------------------------------------------------- | | `konekt:seed` | The 32-byte seed that derives your app’s relay identity. Stable across sessions. | | `konekt:keys` | Symmetric keys for the pairing and session topics. | | `konekt:session` | The approved session, including its namespaces and expiry. | These are connection secrets. Anything that can read them can send requests as your app for the life of the session, so keep them out of logs and error reports. ### Choose a different store [Section titled “Choose a different store”](#choose-a-different-store) `storage` accepts any object with async `getItem`, `setItem`, and `removeItem`: ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], storage: { getItem: async (key) => sessionStorage.getItem(key), setItem: async (key, value) => sessionStorage.setItem(key, value), removeItem: async (key) => sessionStorage.removeItem(key), }, }); ``` Two shortcuts cover the common cases: * `storage: null` disables persistence. Every page load starts from a fresh pairing, and nothing is written to the browser. * `memoryStorage()` from `konekt` gives an isolated in-memory store, which is what tests usually want. Outside a browser, where `localStorage` does not exist, Konekt falls back to memory storage. Sessions then last only as long as the process. ## Restoring a session [Section titled “Restoring a session”](#restoring-a-session) `Provider.init()` reads the stored session before it resolves. When one exists, the returned provider is already connected: `provider.connected` is `true`, the EVM adapter has populated `accounts` and `chainId`, and no `display_uri` is emitted. ```ts const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet] }); if (provider.connected) { showAccount(provider.accounts[0]); } else { showConnectButton(); } ``` This is why a connect button should check `provider.connected` before calling `connect()`. Calling `connect()` on a restored session starts a second pairing the user does not need. Check the expiry yourself Konekt restores a stored session without comparing `session.expiry` to the current time, so an expired session can come back as `connected`. The first wallet request then fails. If your UI depends on the session being usable, check it after `init()`: ```ts const expiry = provider.session?.expiry; if (expiry && expiry * 1000 < Date.now()) { await provider.disconnect(); } ``` ## Ending a session [Section titled “Ending a session”](#ending-a-session) `provider.disconnect()` tells the wallet, clears the stored session, closes the relay socket, and emits `disconnect`. The wallet can also end the session on its side, which emits the same event. ```ts provider.on("disconnect", ({ code, message }) => { clearWalletState(); }); ``` Use `disconnect` as the single place that clears connected state, so wallet-initiated and app-initiated endings take the same path. Reconnecting needs a new provider `disconnect()` closes that provider’s relay client for good. A later `connect()` on the same instance emits `display_uri`, then rejects with `relay closed` instead of pairing, so the QR appears and immediately fails. Because `Provider.init()` always returns the same singleton, a page offering disconnect followed by reconnect should either reload after disconnecting, or manage its own instance with `Provider.create()` and build a fresh one for the next connection: ```ts let provider = await Provider.create({ projectId, metadata, chains: [ethereumMainnet] }); async function disconnect() { await provider.disconnect(); provider = await Provider.create({ projectId, metadata, chains: [ethereumMainnet] }); } ``` Re-register your event listeners on the new instance. ## Staying connected [Section titled “Staying connected”](#staying-connected) The relay client keeps itself alive while a session exists: * a dropped socket is retried every 5 seconds; * retries pause while the document is hidden and resume on `visibilitychange`; * a retry is also triggered by the browser’s `online` event; * pending requests reject with `relay closed` when the socket drops, so surface a retry rather than waiting forever. One failure is not retried. If the relay rejects your credentials it closes with code 3000, and Konekt marks the connection fatal and reports `relay rejected auth`. That almost always means an invalid or unauthorized `projectId`. See [Troubleshooting](../troubleshooting/). ## Protocol lifetimes [Section titled “Protocol lifetimes”](#protocol-lifetimes) `ttl` overrides WalletConnect timeouts, in seconds. Omitted fields keep their defaults: | Field | Default | Effect | | ------------ | ---------------- | ------------------------------------------------------------------------------------------ | | `propose` | 300 (5 minutes) | How long a pairing QR stays valid. `connect()` rejects with `proposal expired` afterwards. | | `request` | 900 (15 minutes) | How long the wallet has to answer. The request rejects with `request expired` afterwards. | | `session` | 86400 (24 hours) | Lifetime of a settled session. | | `minPublish` | 300 | Minimum relay storage window for a published message. | ```ts const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], ttl: { propose: 120 }, }); ``` Shortening `propose` makes an abandoned QR expire sooner. Shortening `request` gives up on a wallet faster, at the cost of failing users who take their time approving. ## Diagnostics [Section titled “Diagnostics”](#diagnostics) `onDebug` receives structured events describing relay and protocol progress. Payload contents are never included, so it is safe to record shapes and timings: ```ts const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], onDebug: (event) => { if (event.type === "error") reportError(event.error); }, }); ``` | Event | Meaning | | -------------- | -------------------------------------------- | | `socket_open` | The relay socket opened. | | `socket_close` | The socket closed, with `code` and `reason`. | | `publish` | A message was published to a `topic`. | | `inbound` | A message arrived on a `topic`. | | `settle` | The wallet approved the session. | | `error` | A relay or protocol error, as a string. | For local work, setting the `WC_DEBUG=1` environment variable prints a truncated protocol trace to the console. That is a development aid; use `onDebug` in production. ## Testing without a relay [Section titled “Testing without a relay”](#testing-without-a-relay) `Provider.create()` returns a new instance every time instead of the singleton, and accepts injected dependencies: ```ts import { Provider, memoryStorage } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; const provider = await Provider.create( { projectId: "test", metadata, chains: [ethereumMainnet] }, { session: fakeSession, storage: memoryStorage() }, ); ``` Injecting `session` keeps the provider offline: Konekt does not open a relay socket at all. You can also inject `relay` to exercise the session protocol against a fake transport, or `seed` to make the relay identity deterministic. # Troubleshooting > Every error Konekt throws, what causes it, and how to fix it. Konekt reports failures two ways. Request failures are `ProviderRpcError` values with an EIP-1193 `code`. Connection and relay failures are plain `Error` values identified by their message. ## Provider error codes [Section titled “Provider error codes”](#provider-error-codes) `ProviderRpcError` carries a `code` from `RpcErrorCode` and a message naming the specific problem. | Code | Name | Cause | Fix | | -------- | ------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | | `4100` | `unauthorized` | A wallet method was called with no session. | Await `connect()`, or check `provider.connected`, before requesting. | | `4200` | `unsupportedMethod` | The method is unknown, the wallet declined it during approval, or an EVM read has no `read` transport. | Read the message; it names the method and lists what the wallet approved. | | `-32602` | `invalidParams` | Malformed `params`, or a `chainId` that is not in `chains`. | Check the method’s parameters, and add the chain to `chains` before targeting it. | Wallet-side rejections keep the wallet’s own code, commonly `4001` for “user rejected the request”. Do not assume every rejection is one of the codes above. ```ts import { ProviderRpcError } from "konekt"; async function sign(message: string) { try { return await provider.request({ method: "personal_sign", params: [message, account] }); } catch (error) { if (error instanceof ProviderRpcError && error.code === 4100) { showConnectButton(); return; } throw error; } } ``` ## Connection errors [Section titled “Connection errors”](#connection-errors) These reject `connect()` or a pending request. Match on the message. | Message | Meaning | What to do | | ------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `relay rejected auth` | The relay refused the connection, closing with code 3000. | Almost always a `projectId` that is missing, wrong, or not authorized for this origin. This one is not retried. | | `relay closed` | The socket dropped, or the provider was already disconnected. | Retry the action. If it happens right after a `disconnect()`, see [reconnecting needs a new provider](../sessions/#ending-a-session). | | `relay connect timeout` | The socket did not open within 10 seconds. | Check network reachability and any proxy or content-security policy blocking `wss://`. | | `relay socket error` | The WebSocket failed to open. | Same causes as a timeout; often an offline device or blocked origin. | | `proposal expired` | Nobody approved the pairing within `ttl.propose`, 5 minutes by default. | Show a fresh QR. Shorten `ttl.propose` if you want to expire abandoned QRs sooner. | | `request expired` | The wallet did not answer within `ttl.request`, 15 minutes by default. | Ask the user to open their wallet. `request_sent` gives you a URL to send them there. | | `disconnected` | The session ended while the request was pending. | Clear connected state and let the user reconnect. | | `UNSUPPORTED_CHAINS` | `chains` was empty. | Pass at least one `Chain`, as `chains: [ethereumMainnet]` or `chains: [solanaMainnet]`. | | `Web Crypto unavailable` | The runtime exposes no Web Crypto. | Serve over HTTPS or `localhost`. Web Crypto is unavailable in insecure browser contexts. | An aborted connection is not in this table because it is not an error condition. `connect({ signal })` rejects with a `DOMException` named `AbortError` when you abort the controller. Handle it as a cancellation: ```ts async function connectWallet(signal: AbortSignal) { try { await provider.connect({ signal }); } catch (error) { if (error instanceof DOMException && error.name === "AbortError") return; showConnectionError(error); } } ``` ## Common situations [Section titled “Common situations”](#common-situations) ### The QR appears and immediately fails with `relay closed` [Section titled “The QR appears and immediately fails with relay closed”](#the-qr-appears-and-immediately-fails-with-relay-closed) The provider was disconnected earlier. `disconnect()` closes the relay client permanently, and `Provider.init()` keeps returning that same instance. Reload the page after disconnecting, or manage instances with `Provider.create()`. See [Sessions](../sessions/#ending-a-session). ### No QR appears when the user clicks connect [Section titled “No QR appears when the user clicks connect”](#no-qr-appears-when-the-user-clicks-connect) A stored session was restored, so the provider is already connected and does not need a pairing. Check `provider.connected` and show the connected state instead of a QR. ### `4100` on the first request after a page reload [Section titled “4100 on the first request after a page reload”](#4100-on-the-first-request-after-a-page-reload) The stored session did not restore. Common causes: `storage: null`, a private-browsing context where `localStorage` throws, a different origin than the one that paired, or the user clearing site data. Read `provider.connected` after `Provider.init()` rather than assuming a session exists. ### The session restored, but every request fails [Section titled “The session restored, but every request fails”](#the-session-restored-but-every-request-fails) The stored session is past its expiry. Konekt restores it without checking the clock. Compare `provider.session?.expiry` against the current time after `init()` and disconnect if it has passed. See [Sessions](../sessions/#restoring-a-session). ### `connect()` never resolves and never rejects [Section titled “connect() never resolves and never rejects”](#connect-never-resolves-and-never-rejects) The `AbortSignal` was already aborted when you passed it. Konekt subscribes to the signal after publishing the proposal, so an abort that already happened is never observed, and the attempt waits out `ttl.propose`. Create the `AbortController` when the user starts connecting, not before. ### `eth_getBalance` fails with `4200` but signing works [Section titled “eth\_getBalance fails with 4200 but signing works”](#eth_getbalance-fails-with-4200-but-signing-works) Reads need a transport. Wallet methods go to the wallet; JSON-RPC reads go to the chain’s `read`: ```ts import { http } from "konekt/http"; import { evm } from "konekt/eip155"; chains: [evm(1, { read: http("https://ethereum.example-rpc.com") })]; ``` Configure `read` on every EVM chain you read from. See [Chains and networks](../chains/#add-json-rpc-reads). ### The wallet is on a chain your app did not configure [Section titled “The wallet is on a chain your app did not configure”](#the-wallet-is-on-a-chain-your-app-did-not-configure) `chainChanged` reaches your listener with the wallet’s chain, but `provider.chainId` stays on a configured chain, so requests and reads never target a network you did not configure. Configure every network your app supports, and show an unsupported-network state when the event names an unknown chain. ### `chainChanged` gives a value that is not hex [Section titled “chainChanged gives a value that is not hex”](#chainchanged-gives-a-value-that-is-not-hex) Konekt emits hex, but a wallet’s own event is forwarded unchanged, so `"1"` can reach your listener. Use `Number(chainId)`, which reads both forms. ### The wallet approved fewer chains or methods than requested [Section titled “The wallet approved fewer chains or methods than requested”](#the-wallet-approved-fewer-chains-or-methods-than-requested) `chains` is what your app proposed, not what the wallet granted. Read `provider.session?.namespaces` for what was actually approved, and `provider.accountsByChain` for the addresses. A method the wallet declined fails locally with `4200`. ### Nothing happens on mobile after sending a request [Section titled “Nothing happens on mobile after sending a request”](#nothing-happens-on-mobile-after-sending-a-request) The app must open the wallet. Listen for `request_sent` and navigate to its `url`: ```ts provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); ``` `url` is `undefined` when the wallet advertised no redirect. See [Wallet UI](../wallet-ui/#open-the-wallet-for-a-request). ### Tapping a wallet on an iPhone only shows a QR code [Section titled “Tapping a wallet on an iPhone only shows a QR code”](#tapping-a-wallet-on-an-iphone-only-shows-a-qr-code) Your UI is fetching the pairing URI after the tap. WebKit refuses to leave for a wallet’s custom scheme once the gesture that asked for it has expired, so a redirect issued after the relay round trip is dropped without an error. Start pairing before the user chooses a wallet, then call `openWalletLink()` inside the tap handler. `WalletModal` does this for you; [Build your own UI](../custom-ui/#step-3-your-wallet-list) covers it for a custom picker. ### An injected wallet is offered in a browser that has none [Section titled “An injected wallet is offered in a browser that has none”](#an-injected-wallet-is-offered-in-a-browser-that-has-none) A wagmi config registers `injected()` whether or not an extension answers, and mobile Safari usually has none. `useWagmiPairing` lists an injected connector only while `getProvider()` resolves, so upgrade konekt-ui if a dead “Installed” row appears; a custom picker should make the same check. ## Getting more detail [Section titled “Getting more detail”](#getting-more-detail) Pass `onDebug` to see relay and protocol events without exposing payload contents: ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], onDebug: (event) => console.log(event), }); ``` During local development, `WC_DEBUG=1` prints a truncated protocol trace. See [Diagnostics](../sessions/#diagnostics). If the problem looks like a bug in Konekt, open an issue with the `onDebug` output, the wallet and its version, and a minimal reproduction: [github.com/lsheva/konekt/issues](https://github.com/lsheva/konekt/issues). # Frameworks and SSR > Initialize Konekt safely in Vite, Next.js, and other server-rendered React apps. Konekt is a browser library. It needs Web Crypto, `WebSocket`, and `localStorage`, and every guide’s setup snippet assumes a browser is present. In a server-rendered app, keep initialization on the client. ## What breaks on the server [Section titled “What breaks on the server”](#what-breaks-on-the-server) | Dependency | Server behavior | | -------------------------------------- | ---------------------------------------------------------------------------------------------- | | `window.location.origin` in `metadata` | Throws. `window` does not exist. | | `localStorage` | Missing, so Konekt silently falls back to memory storage and no session persists. | | Web Crypto | Missing outside a secure context, and `Provider.init()` rejects with `Web Crypto unavailable`. | | `WebSocket` | Missing, so the relay cannot connect. | None of this is a problem as long as `Provider.init()` runs in the browser. The mistake to avoid is calling it at module scope in a file the server also evaluates. ## Vite and other client-only apps [Section titled “Vite and other client-only apps”](#vite-and-other-client-only-apps) Nothing special is required. Top-level initialization works because the module only ever runs in a browser: src/provider.ts ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; export const provider = await Provider.init({ projectId: import.meta.env.VITE_WC_PROJECT_ID, metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ethereumMainnet], }); ``` Even here, consider initializing on demand instead so visitors who never connect a wallet do not download the provider. See [Bundle size and loading](../bundle-size/). ## Next.js App Router [Section titled “Next.js App Router”](#nextjs-app-router) Create the provider inside a client component, after mount. This hook keeps it out of the server render and still initializes only once: ```tsx "use client"; import { useEffect, useState } from "react"; import type { Provider } from "konekt"; let providerPromise: Promise | undefined; function getProvider() { providerPromise ??= (async () => { const [{ Provider }, { ethereumMainnet }] = await Promise.all([ import("konekt"), import("konekt/eip155"), ]); return Provider.init({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID as string, metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ethereumMainnet], }); })().catch((error) => { providerPromise = undefined; throw error; }); return providerPromise; } export function useKonekt() { const [provider, setProvider] = useState(); useEffect(() => { let active = true; void getProvider().then((p) => { if (active) setProvider(p); }); return () => { active = false; }; }, []); return provider; } ``` The dynamic imports mean no Konekt code is evaluated during the server render, and clearing a rejected promise lets a failed attempt be retried. `useProviderPairing()` accepts `undefined`, so a wallet button can render before the provider is ready: ```tsx "use client"; import { useState } from "react"; import { useProviderPairing, WalletModal } from "konekt-ui"; import "konekt-ui/styles.css"; import { useKonekt } from "./useKonekt"; export function ConnectWallet() { const provider = useKonekt(); const pairing = useProviderPairing(provider); const [open, setOpen] = useState(false); return ( <> setOpen(false)} /> ); } ``` Starting a pairing before the provider exists shows a readable error rather than crashing. ### Set `metadata.url` to your real origin [Section titled “Set metadata.url to your real origin”](#set-metadataurl-to-your-real-origin) Wallets display this URL during approval, and SIWE binds to it. `window.location.origin` is correct in the browser, but hardcode the production origin if you also build the metadata anywhere the server can reach: ```ts const url = process.env.NEXT_PUBLIC_APP_URL ?? window.location.origin; ``` The `domain` and `uri` passed to [`siwe()`](../features/) must match what your server checks with `checkClaims()`, so keep both derived from the same value. ## Next.js with wagmi [Section titled “Next.js with wagmi”](#nextjs-with-wagmi) Follow the [wagmi guide](../wagmi/) for the connector, then make the config SSR-aware. This works with React 18 or 19 and wagmi 2 or 3. Wagmi needs `ssr: true` so it hydrates from cookies instead of assuming browser storage: ```ts "use client"; import { cookieStorage, createConfig, createStorage, http } from "wagmi"; import { base, mainnet } from "wagmi/chains"; import { injected } from "wagmi/connectors"; import { konekt } from "konekt-ui/wagmi"; const konektOptions = { projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID as string, metadata: { name: "My app", description: "Connect to My app", url: "https://app.example.com", icons: ["https://app.example.com/icon.png"], }, }; export const config = createConfig({ chains: [mainnet, base], connectors: [injected(), konekt(konektOptions)], storage: createStorage({ storage: cookieStorage }), ssr: true, transports: { [mainnet.id]: http(), [base.id]: http(), }, }); ``` The Konekt connector imports Konekt lazily inside `getProvider()`, so registering it in a config that the server also evaluates does not pull the provider into the server bundle. It initializes when wagmi first asks the connector whether it is authorized, which happens in the browser. Mark the file that calls `createConfig()` and the component rendering `WagmiProvider` as `"use client"`. ## React Native and other non-browser runtimes [Section titled “React Native and other non-browser runtimes”](#react-native-and-other-non-browser-runtimes) Konekt targets browsers. A runtime without Web Crypto, `WebSocket`, and a storage implementation is not supported. In Node, where `localStorage` is absent, Konekt falls back to memory storage, so sessions last only as long as the process. Pass your own `storage` to persist them. See [Sessions](../sessions/#choose-a-different-store). # Bundle size and loading > Measured bundle sizes, tree-shaking rules, and lazy-loading patterns for keeping wallet code off the initial path. Konekt separates the provider, chain adapters, read transport, authentication, and React UI into public entry points. Your app can ship only the parts it uses—and can delay the whole wallet stack until someone opens the connect flow. ## Measured sizes [Section titled “Measured sizes”](#measured-sizes) These are production bundle measurements from the repository’s `pnpm size` check: | Import | Minified + gzip | | ------------------------------------ | --------------- | | `Provider` + `evm` initial chunk | 10.00 kB | | ChaCha20-Poly1305 lazy chunk | 4.84 kB | | Ed25519/X25519 compatibility chunk | 13.80 kB | | SHA-256/HKDF compatibility chunk | 2.96 kB | | `http` | 253 B | | `siwe` + `cacaosOf` | 844 B | | `verifyCacao` + `checkClaims` | 17.36 kB | | `solana` + `solanaMainnet` | 752 B | | `WalletModal` + `useProviderPairing` | 10.30 kB | | wagmi `ConnectButton` | 11.89 kB | | `konekt-ui/styles.css` | 2.98 kB | The provider uses Web Crypto for Ed25519, X25519, SHA-256, and HKDF. Its initial row excludes the Noble compatibility chunks, which are loaded automatically only when a platform operation is unavailable. WalletConnect encryption uses a lazy ChaCha20-Poly1305 chunk because browsers do not standardize that cipher in Web Crypto; it loads on the first encrypted protocol message. Current native secure-curve support starts with Chrome 137, Firefox 130, and Safari 18.4. Older runtimes continue to work through the compatibility chunk. Web Crypto requires a secure browser context such as HTTPS or localhost. The check bundles the listed exports and their runtime dependencies with esbuild, minifies the result, and reports gzip transfer size. The UI rows include the QR encoder but exclude peer dependencies such as React, viem, and wagmi; those libraries may already be shared by the application. Each row is measured independently, so do not add the rows to predict an application bundle—your bundler can share and deduplicate modules. Exact output varies with dependency and bundler versions. The committed lockfile and [size configuration](https://github.com/lsheva/konekt/blob/main/.size-limit.mts) make the repository result reproducible and enforce limits: ```sh pnpm size ``` ## Compared in a real Vite app [Section titled “Compared in a real Vite app”](#compared-in-a-real-vite-app) The table above is each Konekt import on its own, without React. The headless path through the first encrypted message is 14.84 kB, the wallet modal and styles are 13.28 kB, and together they are a 28.11 kB connect stack. The numbers that show up in a browser are larger, and so is the gap versus the official stack, because `@walletconnect/ethereum-provider` and AppKit emit many extra chunks that package-main-bundle tools omit. Four matched React apps in this repository each connect Ethereum and show an address. They share Vite, React 19, and the same tiny shell. `react` and `react-dom` are marked external, so the totals are the wallet stack. The only other difference is which wallet library each app imports: | App | First load | Overall | | ---------------------- | ---------- | ---------- | | WalletConnect | 145.74 kB | 538.06 kB | | WalletConnect + AppKit | 721.26 kB | 1079.28 kB | | Konekt | 11.03 kB | 33.76 kB | | Konekt + UI | 19.06 kB | 45.52 kB | * `packages/size-walletconnect` — `@walletconnect/ethereum-provider@2.23.10`, `showQrModal: false` * `packages/size-appkit` — `@reown/appkit@1.8.23` with the ethers adapter, email, socials, swaps, on-ramp, and analytics turned off * `packages/size-konekt` — `Provider` + `evm` * `packages/size-konekt-ui` — the same provider plus `WalletModal` First load is the JavaScript and CSS the production `index.html` requests: the entry script, stylesheets, and modulepreloads. Overall is every JS, CSS, WASM, and font file Vite emitted. Each file is minified, gzipped at level 9, then summed. Headless Konekt is **92.4%** smaller on first load and **93.7%** smaller overall than the official Ethereum Provider. Konekt with UI is **97.4%** smaller on first load and **95.8%** smaller overall than AppKit. The Ethereum Provider still emits AppKit modal chunks as dynamic imports even with `showQrModal: false`, which is why its overall size is far above its first load. AppKit’s first load stays large because `createAppKit()` module-preloads wallet lists, email inputs, and related UI even when those features are disabled. The Konekt apps leave Noble compatibility chunks off the first load, the same way a modern-browser session would. ```sh pnpm size:apps ``` See [Why Konekt is better](../why-konekt/) for the architectural comparison and [Konekt UI](../konekt-ui/#konekt-ui-vs-reown-appkit) for the direct UI feature comparison. ## Let tree-shaking work [Section titled “Let tree-shaking work”](#let-tree-shaking-work) The `konekt` package declares that its modules have no top-level side effects. `konekt-ui` marks only its CSS as side-effectful. A production ESM bundler can therefore remove exports and modules that are not reachable from your application. Import from the narrow public entry point: ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; ``` Then add optional code only where it is needed: ```ts import { http } from "konekt/http"; // Browser JSON-RPC reads through Provider import { siwe } from "konekt/siwe"; // Authentication during pairing ``` Keep these boundaries in mind: * Do not import every adapter through a local “export everything” barrel. * If viem or wagmi already handles public reads, omit `konekt/http`. * Keep `konekt/cacao` on the server that verifies authentication. Importing it in browser code adds signature-verification code without creating a trustworthy browser-side check. * Import `konekt-ui/styles.css` only when using the styled React components. * Check a production build. Development module counts and source-file sizes are not bundle sizes. ## Lazy-load the provider [Section titled “Lazy-load the provider”](#lazy-load-the-provider) If wallet state is not needed during the first render, load Konekt when the user opens the connect flow: ```ts import type { Provider } from "konekt"; let providerPromise: Promise | undefined; async function initializeProvider() { const [{ Provider }, { ethereumMainnet }] = await Promise.all([ import("konekt"), import("konekt/eip155"), ]); return Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ethereumMainnet], }); } export function getProvider() { if (!providerPromise) { providerPromise = initializeProvider().catch((error) => { providerPromise = undefined; throw error; }); } return providerPromise; } ``` The type-only import is erased from the browser output. Caching the promise prevents two quick clicks from creating competing initialization work, while clearing a rejected promise lets the user retry. This changes behavior as well as loading time: a saved session is not restored until `getProvider()` runs. Initialize eagerly when the page must show the connected account immediately. Chains and features must be present on the first `Provider.init()` call. The provider is a process singleton and later calls do not add options. To lazy-load SIWE, for example, import it inside `initializeProvider()` and pass it in that same call; do not try to attach it after initialization. ## Lazy-load the React wallet UI [Section titled “Lazy-load the React wallet UI”](#lazy-load-the-react-wallet-ui) Put wallet-only imports in their own component: WalletDialog.tsx ```tsx import type { Provider } from "konekt"; import { useProviderPairing, WalletModal } from "konekt-ui"; import "konekt-ui/styles.css"; export default function WalletDialog(props: { open: boolean; provider: Provider; onClose: () => void; }) { const { provider, ...modalProps } = props; const pairing = useProviderPairing(provider); return ; } ``` Load that component only while it is visible: ```tsx import type { Provider } from "konekt"; import { lazy, Suspense, useState } from "react"; const WalletDialog = lazy(() => import("./WalletDialog")); export function WalletArea(props: { provider: Provider }) { const [open, setOpen] = useState(false); return ( <> {open && ( Loading wallet options…

}> setOpen(false)} />
)} ); } ``` Bundlers such as Vite can place the component JavaScript and CSS in lazy chunks. Keep a visible loading state: downloading code after a click without feedback makes the interface appear broken. For wagmi, statically registering the `konekt-ui/wagmi` connector is still the recommended path. The connector dynamically imports Konekt inside `getProvider()`, so registration itself does not load the provider or open a relay socket. See the [wagmi guide](../wagmi/). ## What to optimize first [Section titled “What to optimize first”](#what-to-optimize-first) 1. Keep server verification out of the browser. 2. Avoid duplicate read clients: use either Konekt’s `read` transport or the viem/wagmi HTTP path when one is sufficient. 3. Lazy-load the connect flow when the initial page does not need restored wallet state. 4. Measure the application’s production output, including shared React, viem, and wagmi chunks. Lazy loading moves bytes to a later request; it does not reduce the total bytes needed after the user opens the wallet flow. Tree-shaking removes code that the application never uses. # viem > Use a connected Konekt provider as a viem custom transport for wallet actions and JSON-RPC reads. Konekt implements the EIP-1193 request interface expected by viem’s `custom()` transport. This lets a viem wallet client sign messages and submit transactions through an approved WalletConnect session. This integration is for EVM networks. Use Konekt directly, or the [Solana](../solana/) and [CosmJS](../cosmjs/) bridges, for other namespaces. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt viem ``` You also need a WalletConnect project ID and an EVM JSON-RPC URL. ## Create and connect the provider [Section titled “Create and connect the provider”](#create-and-connect-the-provider) Configure the Konekt provider before creating viem clients: ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; import { http as konektHttp } from "konekt/http"; import { mainnet } from "viem/chains"; const rpcUrl = "https://ethereum.example-rpc.com"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [ evm(mainnet, { read: konektHttp(rpcUrl), }), ], }); provider.on("display_uri", (uri) => { // Render the URI as a QR code, or use WalletModal from konekt-ui. }); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) { await provider.connect(); } ``` The `read` transport handles JSON-RPC reads sent through the provider. Wallet methods such as `personal_sign` and `eth_sendTransaction` still go to the connected wallet. This snippet omits the pairing UI and cancellation for brevity. See [Wallet UI](../wallet-ui/) for rendering the URI, aborting an attempt, and separating a user cancellation from a real failure. ## Create viem clients [Section titled “Create viem clients”](#create-viem-clients) Wrap the connected provider with `custom()`: ```ts import { createPublicClient, createWalletClient, custom, parseEther, verifyMessage, } from "viem"; import { mainnet } from "viem/chains"; const transport = custom(provider); const walletClient = createWalletClient({ chain: mainnet, transport, }); const publicClient = createPublicClient({ chain: mainnet, transport, }); const [account] = await walletClient.getAddresses(); if (!account) throw new Error("The wallet did not approve an account"); ``` Both clients use the Konekt provider: * wallet actions are routed to the WalletConnect session; * public JSON-RPC actions use the `read` transport configured on `evm()`. ## Sign a message [Section titled “Sign a message”](#sign-a-message) ```ts const message = "Sign in to My app"; const signature = await walletClient.signMessage({ account, message, }); const valid = await verifyMessage({ address: account, message, signature, }); ``` `signMessage()` sends `personal_sign` to the wallet. The `request_sent` listener can return the user to a mobile wallet while the request is pending. ## Send a transaction [Section titled “Send a transaction”](#send-a-transaction) ```ts const hash = await walletClient.sendTransaction({ account, to: "0x000000000000000000000000000000000000dEaD", value: parseEther("0.001"), }); const receipt = await publicClient.waitForTransactionReceipt({ hash }); console.log(receipt.status); ``` The wallet approves and broadcasts the transaction. Receipt polling is a read and therefore uses the configured JSON-RPC transport. ## Use viem HTTP for reads instead [Section titled “Use viem HTTP for reads instead”](#use-viem-http-for-reads-instead) It is also valid—and common—to keep public reads outside Konekt: ```ts import { createPublicClient, http as viemHttp } from "viem"; import { mainnet } from "viem/chains"; const httpPublicClient = createPublicClient({ chain: mainnet, transport: viemHttp(rpcUrl), }); ``` Keep the same `walletClient` as above and use this client in place of the earlier `publicClient`. With this arrangement: * `walletClient` uses WalletConnect through Konekt; * `httpPublicClient` reads directly through viem; * the Konekt chain does not need `read` unless other code sends reads through `provider.request()`, so you can drop `konektHttp` from `evm()` entirely. Alias the two `http` imports when you use both `konekt/http` and viem’s `http()` in one module. ## Multiple EVM networks [Section titled “Multiple EVM networks”](#multiple-evm-networks) Give each network its own Konekt read transport, and pass the result as the `chains` option of the `Provider.init()` call above: ```ts import { base, mainnet } from "viem/chains"; const mainnetRpcUrl = "https://ethereum.example-rpc.com"; const baseRpcUrl = "https://base.example-rpc.com"; const chains = [ evm(mainnet.id, { read: konektHttp(mainnetRpcUrl) }), evm(base.id, { read: konektHttp(baseRpcUrl) }), ]; ``` A chain without its own `read` cannot serve JSON-RPC reads, so configure one per network you read from. The standard `custom(provider)` transport uses the provider’s active EVM chain. Switch the wallet before using a viem client configured for another chain: ```ts await walletClient.switchChain({ id: base.id }); const baseWalletClient = createWalletClient({ chain: base, transport: custom(provider), }); ``` A viem client’s `chain` option describes the network but does not switch the Konekt provider by itself. For a read-only client that must always target one configured network without changing the active chain, wrap Konekt’s per-request target: ```ts import type { RequestArguments } from "konekt"; const baseTransport = custom({ request: (args: RequestArguments) => provider.request(args, `eip155:${base.id}`), }); const basePublicClient = createPublicClient({ chain: base, transport: baseTransport, }); ``` ## Keep application state current [Section titled “Keep application state current”](#keep-application-state-current) Viem clients do not subscribe to provider state automatically. Listen to provider events when your application stores the active account or chain: ```ts provider.on("accountsChanged", (accounts) => { const [next] = accounts; if (next) updateSelectedAccount(next); else clearWalletState(); }); provider.on("chainChanged", (chainId) => { // Number() reads both "0x1" and the "1" some wallets send. updateSelectedChain(Number(chainId)); }); provider.on("disconnect", () => { clearWalletState(); }); ``` Framework integrations such as wagmi already maintain this reactive state. Use the [wagmi integration](../wagmi/) when building a React application around wagmi hooks. Use the [ethers integration](../ethers/) when the rest of the app is on ethers v6. # ethers > Use a connected Konekt provider as an ethers v6 BrowserProvider for signing and JSON-RPC reads. Konekt implements the EIP-1193 `request` interface expected by ethers v6 `BrowserProvider`. Wallet methods go through the WalletConnect session. JSON-RPC reads go through the optional `konekt/http` transport on the EVM chain. This integration is for EVM networks. Use Konekt directly, or the [Solana](../solana/) and [CosmJS](../cosmjs/) bridges, for other namespaces. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt ethers ``` You also need a WalletConnect project ID and an EVM JSON-RPC URL. ## Create and connect the provider [Section titled “Create and connect the provider”](#create-and-connect-the-provider) Configure Konekt before wrapping it with ethers. Reads such as `getBalance()` and receipt polling use the chain’s `read` transport: ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; import { http } from "konekt/http"; const rpcUrl = "https://ethereum.example-rpc.com"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [evm(1, { read: http(rpcUrl) })], }); provider.on("display_uri", (uri) => { // Render the URI as a QR code, or use WalletModal from konekt-ui. }); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) { await provider.connect(); } ``` This snippet omits the pairing UI and cancellation for brevity. See [Wallet UI](../wallet-ui/) for rendering the URI, aborting an attempt, and separating a user cancellation from a real failure. ## Wrap the provider with ethers [Section titled “Wrap the provider with ethers”](#wrap-the-provider-with-ethers) ```ts import { BrowserProvider, parseEther, verifyMessage } from "ethers"; const ethersProvider = new BrowserProvider(provider); const signer = await ethersProvider.getSigner(); const address = await signer.getAddress(); ``` `getSigner()` uses `eth_accounts` / `eth_requestAccounts` answered from the approved session. It does not start pairing. Call `provider.connect()` first. ## Sign a message [Section titled “Sign a message”](#sign-a-message) ```ts const message = "Sign in to My app"; const signature = await signer.signMessage(message); const recovered = verifyMessage(message, signature); ``` That sends `personal_sign` to the wallet. The `request_sent` listener can return the user to a mobile wallet while the request is pending. ## Send a transaction [Section titled “Send a transaction”](#send-a-transaction) ```ts const tx = await signer.sendTransaction({ to: "0x000000000000000000000000000000000000dEaD", value: parseEther("0.001"), }); const receipt = await tx.wait(); console.log(receipt?.status); ``` The wallet approves and broadcasts the transaction. Gas estimation, nonce lookup, and receipt polling are reads and therefore use `konekt/http`. Without a `read` transport, those reads fail with error `4200`. You can instead keep public reads on a separate `ethers.JsonRpcProvider(rpcUrl)` and use Konekt only for the signer: ```ts import { JsonRpcProvider } from "ethers"; const reader = new JsonRpcProvider(rpcUrl); ``` In that arrangement the Konekt chain does not need `read` unless other code still sends reads through `provider.request()`. ## Keep application state on the Konekt provider [Section titled “Keep application state on the Konekt provider”](#keep-application-state-on-the-konekt-provider) Ethers clients do not own the WalletConnect session. Listen to the original Konekt provider for account, chain, and disconnect changes: ```ts provider.on("accountsChanged", (accounts) => { const [next] = accounts; if (next) updateSelectedAccount(next); else clearWalletState(); }); provider.on("chainChanged", (chainId) => { // Number() reads both "0x1" and the "1" some wallets send. updateSelectedChain(Number(chainId)); }); provider.on("disconnect", () => { clearWalletState(); }); ``` After `accountsChanged` or `chainChanged`, call `ethersProvider.getSigner()` again if you still need an ethers signer. `BrowserProvider` does not switch the WalletConnect session by itself. To ask the wallet to switch networks, send `wallet_switchEthereumChain` through Konekt or ethers `send()`. ## Multiple EVM networks [Section titled “Multiple EVM networks”](#multiple-evm-networks) Give each network its own Konekt read transport, then switch the wallet before using an ethers signer against another chain: ```ts const mainnetRpcUrl = "https://ethereum.example-rpc.com"; const baseRpcUrl = "https://base.example-rpc.com"; // Pass this as the `chains` option of the Provider.init() call above. const chains = [ evm(1, { read: http(mainnetRpcUrl) }), evm(8453, { read: http(baseRpcUrl) }), ]; await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId: "0x2105" }], // Base, decimal 8453 }); ``` A `BrowserProvider` constructed for one network still talks to whichever chain is active on the Konekt provider. ## Check with a wallet [Section titled “Check with a wallet”](#check-with-a-wallet) Automated tests cover ethers signing and sending against a local chain. Confirm pairing QR cancellation, mobile request redirects, and chain switching with the wallets you support before shipping. # wagmi > Connect a wagmi React app through Konekt and use the optional wallet and account UI. Wagmi needs a connector that translates its connection lifecycle into EIP-1193 provider calls. Konekt supplies the provider; the connector from `konekt-ui/wagmi` adapts it to wagmi. In this setup: * wagmi’s viem HTTP transports handle public reads; * the Konekt connector handles accounts, signatures, transactions, and chain switching; * `konekt-ui/wagmi` can render the connect, account, and network controls. This guide works with React 18 or 19, wagmi 2 or 3, and viem 2. The snippets use hook names that exist in both wagmi versions (`useAccount`, `connect`, `disconnect`, `switchChain`). Wagmi 3 also exports `useConnection` and `mutate` as newer aliases. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt konekt-ui viem wagmi @tanstack/react-query react react-dom ``` You also need a WalletConnect project ID. ## Add the Konekt connector [Section titled “Add the Konekt connector”](#add-the-konekt-connector) `konekt-ui/wagmi` exports the connector: `konekt(options)` for wagmi configuration and `abortPairing()` for cancelling the current proposal. The `konekt` core package still has no wagmi dependency; the connector lives in the entry point that already declares wagmi as a peer. ```ts import { konekt } from "konekt-ui/wagmi"; ``` If your app does not use `konekt-ui`, copy the [connector implementation](https://github.com/lsheva/konekt/blob/main/packages/konekt-ui/src/wagmi/connector.ts) into your application instead — it is one self-contained file. The connector: * creates `Provider.init()` lazily when wagmi first requests it; * configures EVM chains from the wagmi config; * maps Konekt account, chain, and disconnect events into wagmi events; * exposes `display_uri` through the connector’s `message` event; * opens the wallet URL from `request_sent`; * aborts a pending proposal through `abortPairing()`. ## Create the wagmi config [Section titled “Create the wagmi config”](#create-the-wagmi-config) Register the connector next to injected browser wallets. Save this as `src/web3.tsx`: ```tsx import type { PropsWithChildren } from "react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { WagmiProvider, createConfig, http } from "wagmi"; import { base, mainnet } from "wagmi/chains"; import { injected } from "wagmi/connectors"; import { konekt } from "konekt-ui/wagmi"; const projectId = "YOUR_PROJECT_ID"; export const konektOptions = { projectId, metadata: { name: "My app", description: "Connect to My app", url: "https://app.example.com", icons: ["https://app.example.com/icon.png"], }, }; export const config = createConfig({ chains: [mainnet, base], connectors: [ injected(), konekt(konektOptions), ], transports: { [mainnet.id]: http("https://ethereum.example-rpc.com"), [base.id]: http("https://base.example-rpc.com"), }, }); declare module "wagmi" { interface Register { config: typeof config; } } const queryClient = new QueryClient(); export function Web3Provider({ children }: PropsWithChildren) { return ( {children} ); } ``` Registering the connector does not open a relay socket. On mount, wagmi asks every connector whether it is already authorized, which makes this connector dynamically import Konekt and call `Provider.init()`. That restores a saved session, and it connects to the relay only when a saved session exists. The wagmi `transports` are intentionally separate from Konekt’s optional EVM `read` transport. Wagmi sends public reads through its viem clients and sends wallet actions through the active connector. ## Add the complete connect UI [Section titled “Add the complete connect UI”](#add-the-complete-connect-ui) `ConnectButton` renders: * a connect trigger; * installed connectors and WalletConnect Explorer wallets; * a WalletConnect pairing QR; * connected account and balance details; * network switching and disconnect controls. ```tsx import { abortPairing, ConnectButton } from "konekt-ui/wagmi"; import "konekt-ui/styles.css"; export function WalletControls() { return ; } ``` The button reads the WalletConnect project ID from the registered Konekt connector, so there is nothing to configure twice. `onDismiss` matters because closing the modal should also abort the proposal owned by the connector. The modal itself removes its connector event listener; `abortPairing()` stops the underlying Konekt connection. ## Use wagmi hooks [Section titled “Use wagmi hooks”](#use-wagmi-hooks) Once connected, Konekt behaves like the app’s other wagmi connectors: ```tsx import { formatUnits, parseEther } from "viem"; import { useAccount, useBalance, useDisconnect, useSendTransaction, useSwitchChain, } from "wagmi"; import { base } from "wagmi/chains"; export function AccountActions() { const account = useAccount(); const balance = useBalance({ address: account.address }); const transaction = useSendTransaction(); const switching = useSwitchChain(); const disconnecting = useDisconnect(); if (!account.isConnected || !account.address) { return

No wallet connected.

; } return (

{account.address}

{balance.data ? `${formatUnits(balance.data.value, balance.data.decimals)} ${balance.data.symbol}` : "Loading balance…"}

); } ``` The connector forwards the wallet actions to Konekt. Reads such as `useBalance()` continue to use the HTTP transport in the wagmi config. ## Use your own trigger and modal [Section titled “Use your own trigger and modal”](#use-your-own-trigger-and-modal) Use `useWagmiPairing()` when you want to keep your own connect button while reusing the wallet picker: ```tsx import { useState } from "react"; import { WalletModal } from "konekt-ui"; import { abortPairing, useWagmiPairing } from "konekt-ui/wagmi"; export function CustomWalletButton() { const [open, setOpen] = useState(false); const pairing = useWagmiPairing(); return ( <> setOpen(false)} /> ); } ``` ## Static and lazy connector registration [Section titled “Static and lazy connector registration”](#static-and-lazy-connector-registration) Static registration in `createConfig()` is the recommended path, and the one this guide uses. Because the connector imports Konekt lazily inside `getProvider()`, registration costs one dynamic import during wagmi’s reconnect and no relay socket unless a saved session exists. That import still happens for a visitor who never connects a wallet. If you need Konekt entirely absent until someone opens the wallet picker, pass `getWalletConnect` to `ConnectButton` or `useWagmiPairing()` and create the connector on demand: ```tsx import { useCallback, useRef } from "react"; import type { Connector } from "wagmi"; import { useConfig } from "wagmi"; import { abortPairing, ConnectButton, konekt } from "konekt-ui/wagmi"; import { konektOptions } from "./web3"; export function WalletControls() { const config = useConfig(); const connector = useRef(undefined); const getWalletConnect = useCallback(async () => { // `_internal` is wagmi's private API. Registering a connector after // createConfig() has no public equivalent. connector.current ??= config._internal.connectors.setup(konekt(konektOptions)); return connector.current; }, [config]); return ( ); } ``` The `projectId` prop is needed only on this path: with no Konekt connector registered until pairing starts, the wallet picker has nowhere else to read the ID from. This is the trade-off the repository’s [example app](https://github.com/lsheva/konekt/tree/main/packages/example) demonstrates. Weigh it deliberately: `config._internal` is not part of wagmi’s public API and can change in a minor release. Prefer static registration unless the initial-chunk saving matters for your app, and pin your wagmi version if you adopt this pattern. To keep the provider and modal out of the initial page chunk in other ways, follow the [lazy-loading patterns and measured bundle sizes](../bundle-size/). ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### “No WalletConnect connector is registered” [Section titled ““No WalletConnect connector is registered””](#no-walletconnect-connector-is-registered) Import `konekt` from `konekt-ui/wagmi` and add `konekt(konektOptions)` to `createConfig({ connectors })`. The connector’s `id` or `type` must be `"konekt"`. ### The QR closes but pairing continues [Section titled “The QR closes but pairing continues”](#the-qr-closes-but-pairing-continues) Pass `onDismiss={abortPairing}` to `ConnectButton` or `WalletModal`. ### Reads work but signatures do not [Section titled “Reads work but signatures do not”](#reads-work-but-signatures-do-not) Wagmi HTTP transports handle reads without a wallet. Confirm that the Konekt connector is active and that the user approved a session before sending wallet actions. ### A chain is missing [Section titled “A chain is missing”](#a-chain-is-missing) Add it to the wagmi `chains` array and provide its HTTP transport. The connector derives its proposed EVM chains from that config. # Solana > Sign Solana messages and transactions with Konekt using @solana/web3.js or @solana/kit. Konekt’s Solana adapter forwards WalletConnect methods. It does not implement `@solana/web3.js` or `@solana/kit` wallet types. Keep a small application bridge that encodes requests and checks wallet responses. The repository’s tested bridges live in [`packages/integrations/src/solana`](https://github.com/lsheva/konekt/blob/main/packages/integrations/src/solana). Copy them into your app. Do not add a `konekt/solana-client` wrapper. ## Copy the bridge [Section titled “Copy the bridge”](#copy-the-bridge) Copy these four files, keeping their relative layout, because they import each other: ```plaintext src/ bridge/ bytes.ts # base58 and base64 helpers request.ts # the RequestClient type solana/ rpc.ts # WalletConnect method encoding web3.ts # the @solana/web3.js wallet kit.ts # the @solana/kit wallet, if you use Kit ``` They import each other with explicit `.ts` extensions, which TypeScript accepts under `"allowImportingTsExtensions": true` (with `noEmit`) or `"rewriteRelativeImportExtensions": true`. Vite, Next.js, and other bundlers resolve them as written. Change the extensions to `.js` if your setup requires it. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt @scure/base @solana/web3.js ``` `@scure/base` is what `bytes.ts` uses for base58 and base64. For the Kit bridge, add `@solana/kit` instead of or alongside `@solana/web3.js`. You also need a WalletConnect project ID. `@solana/web3.js` v1 needs `Buffer` in the browser The legacy `Transaction` path calls `Buffer.from()`. Vite apps generally need a polyfill such as `vite-plugin-node-polyfills`, or a `globalThis.Buffer` shim, before signing legacy transactions. Kit and `VersionedTransaction` do not need it. ## Create and connect the provider [Section titled “Create and connect the provider”](#create-and-connect-the-provider) ```ts import { Provider } from "konekt"; import { solanaMainnet } from "konekt/solana"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [solanaMainnet], }); // Render this as a QR code. See the Wallet UI guide. const showPairingUri = (uri: string) => console.log(uri); provider.on("display_uri", showPairingUri); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) await provider.connect(); ``` `chains` always takes an array, so a single Solana chain is `[solanaMainnet]`, not `solanaMainnet`. See [Wallet UI](../wallet-ui/) for rendering the pairing URI and cancelling an attempt, and [Plain JavaScript](../vanilla/) for the connection lifecycle. ### Read the approved address [Section titled “Read the approved address”](#read-the-approved-address) Approved addresses are grouped by CAIP-2 ID on `provider.accountsByChain`. A wallet can approve a session without any Solana account, so check before you use one: ```ts const [address] = provider.accountsByChain[solanaMainnet.id] ?? []; if (!address) throw new Error("The wallet approved no Solana account"); ``` That list holds base58 addresses, not public keys as bytes. When a client needs the wallet’s current pubkeys, call `solana_getAccounts` through the bridge’s `solanaPubkeys()` helper in `rpc.ts`. ## Wire encodings [Section titled “Wire encodings”](#wire-encodings) WalletConnect Solana methods use these encodings. The bridges below apply them for you. | Method | Request | Result | | ------------------------------- | -------------------------------------------- | ------------------------------------------------- | | `solana_signMessage` | `message` base58, `pubkey` base58 | `signature` base58 | | `solana_signTransaction` | `transaction` base64 | `signature` base58, optional `transaction` base64 | | `solana_signAllTransactions` | `transactions` base64\[] | `transactions` base64\[] in the same order | | `solana_signAndSendTransaction` | `transaction` base64, optional `sendOptions` | `signature` base58 | Send the serialized transaction bytes in `transaction`. Do not use the deprecated instruction-list parameters; they cannot represent versioned transactions. If `solana_signTransaction` returns only a signature, apply it to the original transaction. If it also returns `transaction`, deserialize that payload and use it. Wallets may add signatures or instructions. ## @solana/web3.js [Section titled “@solana/web3.js”](#solanaweb3js) `konektWeb3Wallet()` from `web3.ts` exposes the wallet interface most Solana code expects: ```ts import { Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js"; import { solanaMainnet } from "konekt/solana"; import { konektWeb3Wallet } from "./bridge/solana/web3.ts"; const rpcUrl = "https://api.mainnet-beta.solana.com"; const connection = new Connection(rpcUrl); const publicKey = new PublicKey(address); const wallet = konektWeb3Wallet(provider, { publicKey, chainId: solanaMainnet.id }); const signature = await wallet.signMessage(new TextEncoder().encode("Sign in to My app")); const transaction = new Transaction().add( SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: publicKey, lamports: 1 }), ); transaction.feePayer = publicKey; transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; const signed = await wallet.signTransaction(transaction); ``` `signTransaction` accepts both legacy `Transaction` and `VersionedTransaction` and returns the same type it received. `signAndSendTransaction` asks the wallet to broadcast instead: ```ts const txSignature = await wallet.signAndSendTransaction(transaction, { skipPreflight: false }); ``` Target another configured Solana chain without changing the active chain. Add it to `chains` first, or the request fails with `-32602`: ```ts import { solanaDevnet, solanaMainnet } from "konekt/solana"; // chains: [solanaMainnet, solanaDevnet] const devnetWallet = konektWeb3Wallet(provider, { publicKey: new PublicKey(address), chainId: solanaDevnet.id, }); ``` ## @solana/kit [Section titled “@solana/kit”](#solanakit) `kit.ts` encodes Kit `Transaction` objects, then decodes the wallet’s signed bytes: ```ts import type { Transaction } from "@solana/kit"; import { solanaMainnet } from "konekt/solana"; import { konektKitWallet } from "./bridge/solana/kit.ts"; declare const transaction: Transaction; const wallet = konektKitWallet(provider, { address, chainId: solanaMainnet.id }); const signature = await wallet.signMessage(new TextEncoder().encode("Sign in to My app")); const signed = await wallet.signTransaction(transaction); ``` Build and compile the Kit transaction as you already do. The bridge only handles WalletConnect encoding, response checks, and deserialization. ## Check with a wallet [Section titled “Check with a wallet”](#check-with-a-wallet) Protocol-shape tests cover encodings, CAIP-2 targeting, legacy and versioned transactions, and malformed responses. They do not prove that a particular mobile wallet signs every method. Confirm QR pairing, cancellation, request redirects, and both transaction types with the wallets you support. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) [Cosmos](../cosmjs/) follows the same bridge pattern with CosmJS signers, and [Everything together](../multichain/) combines Solana with the other ecosystems in one provider. # Bitcoin > Connect to Bitcoin wallets over WalletConnect and send signing requests with the bip122 adapter. The `bip122` adapter proposes the Bitcoin namespace and forwards its methods to the wallet. There are no local answers and no read transport: every supported method is a wallet request. ## Configure the provider [Section titled “Configure the provider”](#configure-the-provider) ```ts import { Provider } from "konekt"; import { bitcoinMainnet } from "konekt/bip122"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [bitcoinMainnet], }); // Render this as a QR code. See the Wallet UI guide. const showPairingUri = (uri: string) => console.log(uri); provider.on("display_uri", showPairingUri); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) await provider.connect(); ``` `chains` always takes an array, so a single Bitcoin chain is `[bitcoinMainnet]`. | Export | CAIP-2 ID | | ---------------- | ----------------------------------------- | | `bitcoinMainnet` | `bip122:000000000019d6689c085ae165831e93` | | `bitcoinTestnet` | `bip122:000000000933ea01ad0ee984209779ba` | | `bitcoinSignet` | `bip122:00000008819873e925422c1ff0f99f7c` | The reference is the genesis block hash prefix. Build another network with `bitcoin(reference)`. ## Read the approved addresses [Section titled “Read the approved addresses”](#read-the-approved-addresses) Approved addresses are grouped by CAIP-2 ID. A wallet can approve a session without a Bitcoin account, so check before using one: ```ts const [address] = provider.accountsByChain[bitcoinMainnet.id] ?? []; if (!address) throw new Error("The wallet approved no Bitcoin account"); ``` `getAccountAddresses` asks the wallet for its full address list, including the public keys and derivation paths that the CAIP-10 session accounts do not carry: ```ts const addresses = await provider.request({ method: "getAccountAddresses" }); ``` ## Supported methods [Section titled “Supported methods”](#supported-methods) The adapter proposes these methods. A wallet may approve a subset. | Method | Purpose | | ------------------------ | -------------------------------------------------------- | | `getAccountAddresses` | Address list with public keys and derivation paths. | | `signMessage` | Sign a message with the key for a given address. | | `signPsbt` | Sign a partially signed Bitcoin transaction. | | `sendTransfer` | Ask the wallet to build, sign, and broadcast a transfer. | | `bip122_signTransaction` | Sign a raw transaction. | Parameters and result shapes are defined by the WalletConnect Bitcoin specification and the wallet, not by Konekt. Konekt passes `params` through unchanged and returns the wallet’s result as `unknown`, so parse it before use: ```ts const result = await provider.request({ method: "signMessage", params: { account: address, message: "Sign in to My app" }, }); if (typeof result !== "object" || result === null || !("signature" in result)) { throw new Error("The wallet returned an unexpected signMessage result"); } ``` Requesting a method the wallet declined during approval fails locally with `4200`, and the message lists what it did approve. ## Address changes [Section titled “Address changes”](#address-changes) The adapter proposes one event, `bip122_addressesChanged`. Forwarding namespaces surface their events through the provider’s `message` event rather than a namespace-specific one: ```ts provider.on("message", ({ type, data }) => { if (type === "bip122_addressesChanged") refreshAddresses(data); }); ``` ## Target another Bitcoin network [Section titled “Target another Bitcoin network”](#target-another-bitcoin-network) Configure every network you use, then target one request without moving the active chain: ```ts import { bitcoinMainnet, bitcoinTestnet } from "konekt/bip122"; // chains: [bitcoinMainnet, bitcoinTestnet] const result = await provider.request( { method: "getAccountAddresses" }, bitcoinTestnet.id, ); ``` Targeting a chain that is not in `chains` fails with `-32602`. ## Check with a wallet [Section titled “Check with a wallet”](#check-with-a-wallet) Bitcoin wallet support for these methods varies more than EVM support does. Confirm pairing, the methods you depend on, PSBT handling, and request redirects with each wallet you intend to support. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) [Sui and custom namespaces](../sui/) builds this same forwarding pattern for a namespace Konekt does not ship, and [Everything together](../multichain/) combines every ecosystem in one provider. # CosmJS > Use Konekt as CosmJS Amino and direct OfflineSigners, with lossless WalletConnect encodings. Konekt’s Cosmos adapter forwards `cosmos_getAccounts`, `cosmos_signAmino`, and `cosmos_signDirect`. It does not implement CosmJS `OfflineSigner` types. Keep small application factories so CosmJS can select Amino or direct signing without mixing the two. Copy the repository’s tested factories from [`packages/integrations/src/cosmjs`](https://github.com/lsheva/konekt/blob/main/packages/integrations/src/cosmjs). Do not add a `konekt/cosmjs` wrapper. ## Copy the bridge [Section titled “Copy the bridge”](#copy-the-bridge) Copy these five files, keeping their relative layout, because they import each other: ```plaintext src/ bridge/ bytes.ts # base64 helpers and response parsing request.ts # the RequestClient type cosmjs/ accounts.ts # cosmos_getAccounts and signature parsing amino.ts # the OfflineAminoSigner direct.ts # the OfflineDirectSigner ``` They import each other with explicit `.ts` extensions, which TypeScript accepts under `"allowImportingTsExtensions": true` (with `noEmit`) or `"rewriteRelativeImportExtensions": true`. Change the extensions to `.js` if your setup requires it. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt @scure/base @cosmjs/amino @cosmjs/proto-signing cosmjs-types ``` `@scure/base` backs the base64 conversions and `cosmjs-types` supplies the `SignDoc` type the direct signer uses. Add your CosmJS client library as well; the examples below use `@cosmjs/stargate`: ```sh pnpm add @cosmjs/stargate ``` You also need a WalletConnect project ID and a Cosmos RPC URL for CosmJS queries. ## Create and connect the provider [Section titled “Create and connect the provider”](#create-and-connect-the-provider) ```ts import { Provider } from "konekt"; import { cosmoshub } from "konekt/cosmos"; const provider = await Provider.init({ projectId: "YOUR_PROJECT_ID", metadata: { name: "My app", description: "Connect to My app", url: window.location.origin, icons: [new URL("/icon.png", window.location.origin).href], }, chains: [cosmoshub], }); // Render this as a QR code. See the Wallet UI guide. const showPairingUri = (uri: string) => console.log(uri); provider.on("display_uri", showPairingUri); provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); if (!provider.connected) await provider.connect(); ``` `chains` always takes an array, so a single Cosmos chain is `[cosmoshub]`, not `cosmoshub`. See [Wallet UI](../wallet-ui/) for rendering the pairing URI and cancelling an attempt. Session accounts are CAIP-10 bech32 addresses. They do not include `algo` or `pubkey`. CosmJS needs both, so the bridges call `cosmos_getAccounts` on the wallet. ## Keep Amino and direct signers separate [Section titled “Keep Amino and direct signers separate”](#keep-amino-and-direct-signers-separate) CosmJS treats a signer with `signDirect` as a direct signer. If one object also has `signAmino`, CosmJS will not use Amino. Export two factories: ```ts import { SigningStargateClient } from "@cosmjs/stargate"; import { cosmoshub } from "konekt/cosmos"; import { konektAminoSigner } from "./bridge/cosmjs/amino.ts"; import { konektDirectSigner } from "./bridge/cosmjs/direct.ts"; const rpcUrl = "https://cosmoshub.example-rpc.com"; const amino = konektAminoSigner(provider, { chainId: cosmoshub.id }); const direct = konektDirectSigner(provider, { chainId: cosmoshub.id }); const aminoClient = await SigningStargateClient.connectWithSigner(rpcUrl, amino); const directClient = await SigningStargateClient.connectWithSigner(rpcUrl, direct); ``` Use the Amino factory for Amino-only wallets. Use the direct factory when the wallet approved `cosmos_signDirect`. Target Osmosis without changing the active chain. Add it to `chains` first, or the request fails with `-32602`: ```ts import { osmosis } from "konekt/cosmos"; // chains: [cosmoshub, osmosis] const osmoSigner = konektDirectSigner(provider, { chainId: osmosis.id }); ``` ## Lossless encodings [Section titled “Lossless encodings”](#lossless-encodings) WalletConnect Cosmos methods are JSON. CosmJS direct sign docs are not. | Field | CosmJS | WalletConnect | | ----------------------------------------- | ------------ | -------------- | | `bodyBytes`, `authInfoBytes` | `Uint8Array` | base64 strings | | `accountNumber` | `bigint` | decimal string | | Amino `account_number`, `sequence`, `fee` | strings | strings | | account `pubkey` | `Uint8Array` | base64 string | Convert account numbers with `bigint.toString()`. Do not pass them through `Number`; values above `Number.MAX_SAFE_INTEGER` would round. The direct bridge encodes those fields on the way out and decodes them on the way back. If the wallet returns a `signed` document, that document is what CosmJS must use. Wallets often change fees. ## Sign with CosmJS [Section titled “Sign with CosmJS”](#sign-with-cosmjs) ```ts const [account] = await direct.getAccounts(); if (!account) throw new Error("The wallet did not return an account"); const recipient = "cosmos1examplerecipientaddress"; const result = await directClient.sendTokens( account.address, recipient, [{ denom: "uatom", amount: "1000" }], "auto", ); ``` `sendTokens` will call `signDirect` or `signAmino` according to which signer you passed. The Konekt `request_sent` listener can open the wallet while that request is pending. ## Check with a wallet [Section titled “Check with a wallet”](#check-with-a-wallet) Tests cover method names, CAIP-2 targeting, base64/bigint conversion, large account numbers, malformed responses, and preserved wallet sign documents. They do not prove Amino or direct support in a given mobile wallet. Confirm pairing, cancellation, redirects, and the signing mode you ship with the wallets you support. ## Where to go next [Section titled “Where to go next”](#where-to-go-next) [Bitcoin](../bitcoin/) forwards its methods without a bridge, [Sui](../sui/) shows how to add a namespace Konekt does not ship, and [Everything together](../multichain/) combines them all in one provider. # Migrating from @walletconnect/ethereum-provider > Translate an existing EthereumProvider.init setup to Konekt option by option. Konekt implements the same EIP-1193 surface as `@walletconnect/ethereum-provider`, so most application code that calls `request()` and listens for events needs no changes. The differences are in configuration, in the QR modal, and in where JSON-RPC reads go. [Why Konekt is better](../why-konekt/) covers the reasoning. This page is the mechanical translation. ## Install [Section titled “Install”](#install) ```sh pnpm remove @walletconnect/ethereum-provider pnpm add konekt ``` Konekt is ESM-only and has no `@walletconnect` runtime dependency. ## The smallest change [Section titled “The smallest change”](#the-smallest-change) Before: ```ts import { EthereumProvider } from "@walletconnect/ethereum-provider"; const provider = await EthereumProvider.init({ projectId, metadata, optionalChains: [1, 137], showQrModal: true, }); ``` After: ```ts import { Provider } from "konekt"; import { ethereumMainnet, polygonMainnet } from "konekt/eip155"; const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet, polygonMainnet], }); provider.on("display_uri", (uri) => renderQrCode(uri)); ``` Both return an EIP-1193 provider. `provider.request()`, `provider.enable()`, `provider.on()`, `provider.session`, and `provider.disconnect()` all keep working. ## Option by option [Section titled “Option by option”](#option-by-option) | `EthereumProvider.init` | Konekt | Notes | | ----------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------- | | `projectId` | `projectId` | Unchanged. | | `metadata` | `metadata` | Unchanged. | | `optionalChains: [1, 137]` | `chains: [ethereumMainnet, polygonMainnet]` | Named chains, or `evm(id)` for the rest. Bare numbers are not accepted. | | `chains: [1]` | `chains: [ethereumMainnet]` | Konekt has no required namespaces; see below. | | `rpcMap: { 1: url }` | `evm(1, { read: http(url) })` | Per chain, and never automatic. | | `showQrModal: true` | `display_uri` event, or `konekt-ui` | See [Wallet UI](../wallet-ui/) and [konekt-ui](../konekt-ui/). | | `qrModalOptions` | `WalletModal` props | See [konekt-ui](../konekt-ui/). | | `optionalMethods`, `optionalEvents` | Adapter-defined | See [Methods and events](#methods-and-events). | | `methods`, `events` | Adapter-defined | Same. | | — | `features: [siwe(...)]` | One-click authentication. See [Authentication](../features/). | | — | `storage`, `relayUrl`, `ttl`, `onDebug` | See [Sessions](../sessions/). | ### Required namespaces are gone [Section titled “Required namespaces are gone”](#required-namespaces-are-gone) `EthereumProvider` builds a required-namespace proposal when you pass `chains`, `methods`, or `events`, which blocks wallets that do not support all of them. Konekt always proposes optional namespaces, which is the behavior Reown itself recommends. Every chain you configure is offered, the wallet approves what it supports, and you read the result: ```ts provider.session?.namespaces.eip155?.accounts; provider.accountsByChain; // { "eip155:1": ["0x…"] } ``` A method the wallet declined then fails locally with `4200` instead of reaching the wallet. ### `rpcMap` becomes an explicit transport [Section titled “rpcMap becomes an explicit transport”](#rpcmap-becomes-an-explicit-transport) This is the biggest behavioral difference. Without `rpcMap`, `EthereumProvider` silently falls back to Reown’s Blockchain API, so `eth_call` and `eth_getBalance` work without your configuring anything, and your users’ reads go to a third-party endpoint. Konekt never does that. A chain without a `read` transport rejects JSON-RPC reads with `4200`: ```ts import { Provider } from "konekt"; import { evm } from "konekt/eip155"; import { http } from "konekt/http"; const provider = await Provider.init({ projectId, metadata, chains: [ evm(1, { read: http("https://ethereum.example-rpc.com") }), evm(137, { read: http("https://polygon.example-rpc.com") }), ], }); ``` If viem, ethers, or wagmi already owns your public reads, skip `konekt/http` entirely and let those libraries keep their own transports. That is the common case, and it keeps 253 bytes out of your bundle. ### `showQrModal` becomes an event [Section titled “showQrModal becomes an event”](#showqrmodal-becomes-an-event) `EthereumProvider` bundled a modal. Konekt reports the pairing URI and lets your app render it: ```ts provider.on("display_uri", (uri) => renderQrCode(uri)); await provider.connect(); ``` For a ready-made React modal, install `konekt-ui`: ```tsx import { useProviderPairing, WalletModal } from "konekt-ui"; import "konekt-ui/styles.css"; const pairing = useProviderPairing(provider); ; ``` `showQrModal` is deprecated upstream in favor of AppKit, so an app still using it has to change something regardless. See [konekt-ui](../konekt-ui/). ## Methods and events [Section titled “Methods and events”](#methods-and-events) `optionalMethods` and `optionalEvents` do not exist. The EVM adapter proposes a fixed list: `eth_sendTransaction`, `personal_sign`, `eth_sign`, `eth_signTransaction`, the four `eth_signTypedData` variants, `eth_accounts`, `eth_requestAccounts`, and `wallet_switchEthereumChain`, with the `chainChanged` and `accountsChanged` events. To propose something else, declare your own namespace with `forwardingNamespace()` and configure it alongside the EVM chains. See [Build a custom namespace](../chains/#build-a-custom-namespace). ## Events [Section titled “Events”](#events) | `EthereumProvider` | Konekt | | ------------------ | --------------------------------------------------------------- | | `display_uri` | `display_uri`, same payload | | `connect` | `connect`, payload `{ chainId?: "0x1" }` | | `disconnect` | `disconnect`, payload `{ code, message }` | | `accountsChanged` | `accountsChanged`, same payload | | `chainChanged` | `chainChanged`, same payload | | `session_event` | `message` for non-EVM namespaces; EVM events are already mapped | | — | `request_sent`, for opening the wallet on mobile | `disconnect` now fires for app-initiated disconnects too `EthereumProvider` emits `disconnect` only when the wallet ends the session, so apps often clear state in two places. Konekt emits it for both, including your own `provider.disconnect()` call. Remove the duplicate cleanup, or you will run it twice. `request_sent` is new and worth adopting. It carries the wallet URL for a pending request so a mobile user can be returned to their wallet: ```ts provider.on("request_sent", ({ url }) => { if (url) window.location.assign(url); }); ``` ## Things to check after migrating [Section titled “Things to check after migrating”](#things-to-check-after-migrating) * **`sendAsync()` is not implemented.** Use `request()`. Callback-style code needs updating. * **Reads.** Anything that previously relied on the Blockchain API fallback now needs a `read` transport or a separate client. This is the most likely source of a `4200` after migrating. * **Reconnecting after disconnect.** `Provider.init()` is a process singleton, and `disconnect()` closes its relay client permanently. See [Sessions](../sessions/#ending-a-session). * **Session storage keys changed**, so users will pair again once. Konekt uses `konekt:seed`, `konekt:keys`, and `konekt:session`. * **Chain switching.** `wallet_switchEthereumChain` is answered locally when the session already includes the target chain, and forwarded to the wallet otherwise. Configure every chain you support. * **Server-side rendering.** Konekt has no server build. See [Frameworks and SSR](../frameworks/). ## With viem, ethers, or wagmi [Section titled “With viem, ethers, or wagmi”](#with-viem-ethers-or-wagmi) Nothing changes structurally. Konekt is still the EIP-1193 provider you hand to those libraries: * viem: `custom(provider)` — see the [viem guide](../viem/); * ethers v6: `new BrowserProvider(provider)` — see the [ethers guide](../ethers/); * wagmi: the `konekt(options)` connector from `konekt-ui/wagmi` — see the [wagmi guide](../wagmi/). If you were using `@walletconnect/ethereum-provider` through wagmi’s built-in `walletConnect()` connector, replace that connector rather than the provider directly. # For AI agents > Reading order, non-negotiable rules, and a map from task to page for coding assistants that integrate konekt. You are integrating **konekt**, a small browser client for WalletConnect v2. This page is a router: it lists the rules that are easy to get wrong and points at the one page that documents each task. Everything else is written once, somewhere else, so the two cannot drift apart. ## Read in this order [Section titled “Read in this order”](#read-in-this-order) 1. This page. 2. [`skills/konekt/SKILL.md`](../skills/konekt/SKILL.md) — provider, chains, requests, features, wallet events. 3. [`skills/konekt-ui/SKILL.md`](../skills/konekt-ui/SKILL.md) — React wallet UI. 4. The guide for the task you were given, from the map below. 5. [`llms-full.txt`](../llms-full.txt) when you need every human-facing guide in one document. 6. Generated [API pages](../api/readme/) when you need the exact type of a specific export. ## Non-negotiable rules [Section titled “Non-negotiable rules”](#non-negotiable-rules) These are the mistakes that compile, run, and then fail in production or leak. Check every generated integration against them. * Applications call `Provider.init(opts)`; it returns a process singleton and the first options win. Tests call `Provider.create(opts, deps?)`. Do not add `createProvider()` or any function that only forwards to a static method. * `chains` takes `Chain` objects from adapter subpaths (`[ethereumMainnet, evm(8453)]`, `[ethereumMainnet, solanaMainnet]`), never bare numeric IDs, and there is no provider-level `rpcUrl`. * JSON-RPC reads need an explicit `read` transport on the chain: `evm(1, { read: http(url) })`. Without one, a read throws `4200` rather than falling back to a public node. * Features are proposal hooks, not wrappers around `request()`. A feature writes its key under `Proposal.requests` and reads the matching key back from `Session.proposalRequestsResponses`. * Never make an authentication decision in the browser. `konekt/siwe` asks and binds; the server calls both `verifyCacao()` and `checkClaims()` from `konekt/cacao` with a single-use nonce it issued. * Keep `konekt/cacao` out of browser bundles, and keep subpath imports intact instead of re-exporting adapters and features through an application barrel. * Konekt reports UI work through events. Register `display_uri` before `connect()`, and let application code — not the library — open wallet URLs. * Do not add `konekt/solana-client` or `konekt/cosmjs`. Solana and CosmJS use application-owned bridges copied into the app. ## Where each task is documented [Section titled “Where each task is documented”](#where-each-task-is-documented) | Task | Page | | -------------------------------------------------------- | --------------------------------------------------------------------------------------- | | React app with wagmi and the ready-made connect button | [Getting started](../guides/getting-started/) | | Custom connect components over the pairing hooks | [Design your own connect UI](../guides/custom-ui/) | | First provider, first connection, no framework | [Plain JavaScript](../guides/vanilla/) | | Chain adapters, read transports, targeting one chain | [Chains and networks](../guides/chains/) | | `Provider.init` options, persistence, expiry, disconnect | [Sessions and options](../guides/sessions/) | | SIWE, CACAO verification, custom features | [Authentication](../guides/features/) | | Pairing URI, wallet redirects, cancelling a connection | [Wallet UI](../guides/wallet-ui/) | | `WalletModal`, `ConnectButton`, pairing hooks | [konekt-ui](../guides/konekt-ui/) | | Next.js, Vite, SSR, client-only initialization | [Frameworks and SSR](../guides/frameworks/) | | Measured sizes, lazy loading, entry-point choice | [Bundle size and loading](../guides/bundle-size/) | | Error codes and thrown messages | [Troubleshooting](../guides/troubleshooting/) | | viem, ethers, wagmi | [viem](../guides/viem/), [ethers](../guides/ethers/), [wagmi](../guides/wagmi/) | | Solana, Bitcoin, CosmJS | [Solana](../guides/solana/), [Bitcoin](../guides/bitcoin/), [CosmJS](../guides/cosmjs/) | | Sui, or any namespace without a shipped adapter | [Sui and custom namespaces](../guides/sui/) | | Several ecosystems in one session | [Everything together](../guides/multichain/) | | Replacing `@walletconnect/ethereum-provider` | [Migration guide](../guides/migrate-ethereum-provider/) | Read the matching page before generating code for that task. Do not infer an API from a neighbouring guide. ## Completion checklist [Section titled “Completion checklist”](#completion-checklist) * The `display_uri` listener is registered before `connect()`, and pairing is aborted when its UI closes. * Chains are adapter objects, and every network that needs reads has its own `read` transport. * Wallet writes go to the wallet; `eth_*`, `net_*`, and `web3_*` reads go to `read`. * Server verification modules are absent from the browser bundle. * Authentication is decided on the server, with a single-use nonce and both CACAO checks. * Wallet URLs are opened by application UI code. # Avatar > **Avatar**(`__namedParameters`): `Element` Address-derived gradient disc used by the account chip and account dialog. Decorative: pair it with a visible address. The disc is `aria-hidden`. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`AvatarProps`](/konekt/api/konekt-ui/src/type-aliases/avatarprops/) ## Returns [Section titled “Returns”](#returns) `Element` # fetchWallets > **fetchWallets**(`opts`): `Promise`<[`FetchWalletsResult`](/konekt/api/konekt-ui/src/type-aliases/fetchwalletsresult/)> Loads one page of wallet listings from WalletConnect Explorer. ## Parameters [Section titled “Parameters”](#parameters) ### opts [Section titled “opts”](#opts) [`FetchWalletsOptions`](/konekt/api/konekt-ui/src/type-aliases/fetchwalletsoptions/) ## Returns [Section titled “Returns”](#returns) `Promise`<[`FetchWalletsResult`](/konekt/api/konekt-ui/src/type-aliases/fetchwalletsresult/)> ## Throws [Section titled “Throws”](#throws) When Explorer returns a non-successful HTTP response. # filterWallets > **filterWallets**(`wallets`, `filter?`): [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/)\[] Applies `include` and `exclude` Explorer ID filters to an existing wallet list. ## Parameters [Section titled “Parameters”](#parameters) ### wallets [Section titled “wallets”](#wallets) readonly [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/)\[] ### filter? [Section titled “filter?”](#filter) [`WalletFilter`](/konekt/api/konekt-ui/src/type-aliases/walletfilter/) ## Returns [Section titled “Returns”](#returns) [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/)\[] # formatWalletLink > **formatWalletLink**(`href`, `uri`): `string` Adds a URL-encoded WalletConnect pairing URI to a wallet’s native or universal base URL. A base without a scheme is treated as a custom native scheme. For example, `"example"` becomes `example://wc?uri=…`. ## Parameters [Section titled “Parameters”](#parameters) ### href [Section titled “href”](#href) `string` ### uri [Section titled “uri”](#uri) `string` ## Returns [Section titled “Returns”](#returns) `string` # isMobile > **isMobile**(): `boolean` Detects a likely touch-first mobile device from its primary pointer. Returns `false` during server rendering. ## Returns [Section titled “Returns”](#returns) `boolean` # Modal > **Modal**(`__namedParameters`): `Element` | `null` Accessible modal shell with focus management and a dismissible backdrop. When open, it moves focus to the first control, traps Tab navigation, closes on Escape, prevents page scrolling, and restores the element that was focused previously. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`ModalProps`](/konekt/api/konekt-ui/src/type-aliases/modalprops/) ## Returns [Section titled “Returns”](#returns) `Element` | `null` # openWalletLink > **openWalletLink**(`href`): `void` Opens a formatted wallet link. Mobile navigation replaces the current page. Desktop navigation opens a protected new tab. Call this inside the event handler of the tap that asked for it: WebKit refuses to leave for a custom scheme once the gesture has expired. ## Parameters [Section titled “Parameters”](#parameters) ### href [Section titled “href”](#href) `string` ## Returns [Section titled “Returns”](#returns) `void` # pairingExpiry > **pairingExpiry**(`uri`): `number` | `undefined` Reads the deadline a WalletConnect pairing URI carries, in unix seconds. The proposal lifetime belongs to the provider, which stamps it into the URI, so UI that has to know when a pairing dies reads it back instead of repeating the number. ## Parameters [Section titled “Parameters”](#parameters) ### uri [Section titled “uri”](#uri) `string` ## Returns [Section titled “Returns”](#returns) `number` | `undefined` `undefined` when the URI carries no `expiryTimestamp` or an unreadable one. # pairingRefreshDelay > **pairingRefreshDelay**(`uri`, `now?`): `number` | `undefined` How long a pairing URI may still be offered, in milliseconds. A wallet cannot answer a lapsed pairing, so UI that keeps one on screen replaces it while there is still time to hand out the next one. ## Parameters [Section titled “Parameters”](#parameters) ### uri [Section titled “uri”](#uri) `string` ### now? [Section titled “now?”](#now) `number` = `...` ## Returns [Section titled “Returns”](#returns) `number` | `undefined` `0` when the URI is spent, or `undefined` when it carries no deadline to work from. # parseListings > **parseListings**(`body`): [`FetchWalletsResult`](/konekt/api/konekt-ui/src/type-aliases/fetchwalletsresult/) Parses a WalletConnect Explorer response, dropping malformed listings. ## Parameters [Section titled “Parameters”](#parameters) ### body [Section titled “body”](#body) `unknown` ## Returns [Section titled “Returns”](#returns) [`FetchWalletsResult`](/konekt/api/konekt-ui/src/type-aliases/fetchwalletsresult/) # parseWallet > **parseWallet**(`value`): [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/) | `undefined` Parses and validates one unknown WalletConnect Explorer listing. ## Parameters [Section titled “Parameters”](#parameters) ### value [Section titled “value”](#value) `unknown` ## Returns [Section titled “Returns”](#returns) [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/) | `undefined` A normalized wallet, or `undefined` when the value has no ID or name. # QrCode > **QrCode**(`__namedParameters`): `Element` Renders a string as an accessible SVG QR code. Encoding loads asynchronously, so the component first renders a square placeholder with the requested size. The finished image is labelled “Pairing QR code”. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`QrCodeProps`](/konekt/api/konekt-ui/src/type-aliases/qrcodeprops/) ## Returns [Section titled “Returns”](#returns) `Element` # truncateAddress > **truncateAddress**(`address`): `string` ## Parameters [Section titled “Parameters”](#parameters) ### address [Section titled “address”](#address) `string` ## Returns [Section titled “Returns”](#returns) `string` # useProviderPairing > **useProviderPairing**(`provider?`, `__namedParameters?`): [`Pairing`](/konekt/api/konekt-ui/src/type-aliases/pairing/) Creates a [Pairing](/konekt/api/konekt-ui/src/type-aliases/pairing/) for a Konekt provider without wagmi. Starting the pairing calls `provider.connect({ signal })` and subscribes to `display_uri`. Running the returned teardown aborts the connection and removes the listener. Provider chains become the modal’s default Explorer filter. Pass `sources` to list injected wallets alongside WalletConnect pairing. Connecting an injected wallet goes to its source and never touches the provider. ## Parameters [Section titled “Parameters”](#parameters) ### provider? [Section titled “provider?”](#provider) [`PairingProvider`](/konekt/api/konekt-ui/src/type-aliases/pairingprovider/) Provider to connect. It may be `undefined` while application setup is loading; the returned pairing reports a readable error if started before the provider exists. ### \_\_namedParameters? [Section titled “\_\_namedParameters?”](#__namedparameters) [`ProviderPairingOptions`](/konekt/api/konekt-ui/src/type-aliases/providerpairingoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`Pairing`](/konekt/api/konekt-ui/src/type-aliases/pairing/) # walletHref > **walletHref**(`wallet`, `uri`, `mobile?`): `string` | `undefined` Formats the pairing URI into the wallet’s link for one platform. ## Parameters [Section titled “Parameters”](#parameters) ### wallet [Section titled “wallet”](#wallet) [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/) ### uri [Section titled “uri”](#uri) `string` ### mobile? [Section titled “mobile?”](#mobile) `boolean` = `...` ## Returns [Section titled “Returns”](#returns) `string` | `undefined` # walletLink > **walletLink**(`wallet`, `mobile?`): `string` | `undefined` The base URL a wallet advertised for one platform, native scheme first. Platforms do not stand in for each other: a listing with only desktop links cannot be reached from a phone, and the caller offers a QR code instead. ## Parameters [Section titled “Parameters”](#parameters) ### wallet [Section titled “wallet”](#wallet) [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/) ### mobile? [Section titled “mobile?”](#mobile) `boolean` = `...` ## Returns [Section titled “Returns”](#returns) `string` | `undefined` `undefined` when the wallet advertised no link for that platform. # WalletModal > **WalletModal**(`__namedParameters`): `Element` Wallet picker and WalletConnect pairing dialog. The modal loads compatible wallets from WalletConnect Explorer and includes any local wallets from the pairing binding. On a desktop browser it starts pairing when the user asks for a QR code, and closing that view runs the teardown returned by `pairing.start`. On a phone it instead pairs as soon as it opens, lists only wallets reachable by a mobile link, and leaves for the wallet inside the tap that chose it. Both are required: WebKit refuses to open a custom scheme once the gesture that asked for it has expired, so the URI cannot be fetched first. A pairing that is about to lapse is replaced with a fresh one. The dialog traps keyboard focus, closes on Escape, restores previous focus, and labels its controls for assistive technology. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`WalletModalProps`](/konekt/api/konekt-ui/src/type-aliases/walletmodalprops/) ## Returns [Section titled “Returns”](#returns) `Element` # AvatarProps > **AvatarProps** = `Pick`<[`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/), `"className"` | `"style"` | `"unstyled"`> & `object` Props for [Avatar](/konekt/api/konekt-ui/src/functions/avatar/). ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### address [Section titled “address”](#address) > **address**: `string` Account address the gradient is derived from. ### size? [Section titled “size?”](#size) > `optional` **size?**: `number` Rendered width and height in CSS pixels. Defaults to 28. # ExplorerWallet > **ExplorerWallet** = `object` Normalized wallet listing returned by [fetchWallets](/konekt/api/konekt-ui/src/functions/fetchwallets/). ## Properties [Section titled “Properties”](#properties) ### desktop [Section titled “desktop”](#desktop) > **desktop**: [`WalletLinks`](/konekt/api/konekt-ui/src/type-aliases/walletlinks/) Links advertised for desktop platforms. *** ### id [Section titled “id”](#id) > **id**: `string` WalletConnect Explorer ID. *** ### imageUrl [Section titled “imageUrl”](#imageurl) > **imageUrl**: `string` Best available wallet image URL. *** ### mobile [Section titled “mobile”](#mobile) > **mobile**: [`WalletLinks`](/konekt/api/konekt-ui/src/type-aliases/walletlinks/) Links advertised for mobile platforms. *** ### name [Section titled “name”](#name) > **name**: `string` Human-readable wallet name. *** ### rdns [Section titled “rdns”](#rdns) > **rdns**: `string` Reverse-domain identifier used to match an installed EIP-6963 wallet. # FetchWalletsOptions > **FetchWalletsOptions** = `object` Query options for [fetchWallets](/konekt/api/konekt-ui/src/functions/fetchwallets/). ## Properties [Section titled “Properties”](#properties) ### chains? [Section titled “chains?”](#chains) > `optional` **chains?**: readonly `string`\[] CAIP-2 chain IDs. Results must support at least one. *** ### entries? [Section titled “entries?”](#entries) > `optional` **entries?**: `number` Maximum number of entries to request. *** ### ids? [Section titled “ids?”](#ids) > `optional` **ids?**: readonly `string`\[] Exact WalletConnect Explorer IDs to request. *** ### page? [Section titled “page?”](#page) > `optional` **page?**: `number` One-based result page. *** ### projectId [Section titled “projectId”](#projectid) > **projectId**: `string` WalletConnect Cloud project ID. *** ### search? [Section titled “search?”](#search) > `optional` **search?**: `string` Wallet-name search text. # FetchWalletsResult > **FetchWalletsResult** = `object` One normalized page from WalletConnect Explorer. ## Properties [Section titled “Properties”](#properties) ### total [Section titled “total”](#total) > **total**: `number` Total matching entries reported by Explorer. *** ### wallets [Section titled “wallets”](#wallets) > **wallets**: [`ExplorerWallet`](/konekt/api/konekt-ui/src/type-aliases/explorerwallet/)\[] Valid wallet entries parsed from the response. # LocalWallet > **LocalWallet** = `object` A wallet the browser already has: an injected extension, or any connector the app registered. ## Properties [Section titled “Properties”](#properties) ### icon? [Section titled “icon?”](#icon) > `optional` **icon?**: `string` Optional wallet icon URL. *** ### id [Section titled “id”](#id) > **id**: `string` Stable connector-specific identifier. *** ### name [Section titled “name”](#name) > **name**: `string` Human-readable wallet name. *** ### rdns? [Section titled “rdns?”](#rdns) > `optional` **rdns?**: `string` EIP-6963 rdns when the wallet announced one. Used to dedupe against explorer listings. # LocalWalletSource > **LocalWalletSource** = `object` A discovery source for wallets the browser already has. Each source owns its wallets: [useProviderPairing](/konekt/api/konekt-ui/src/functions/useproviderpairing/) routes a clicked wallet back to the source whose `wallets` contains it. `konekt-ui/wallet-standard` supplies Solana extensions and `konekt-ui/cosmos` supplies Keplr-API extensions; an app can add its own source for anything else. What happens after `connect` — accounts, signing, disconnects — stays with the source owner, not with the modal. ## Properties [Section titled “Properties”](#properties) ### connect [Section titled “connect”](#connect) > **connect**: (`wallet`) => `void` Connects one of `wallets`. #### Parameters [Section titled “Parameters”](#parameters) ##### wallet [Section titled “wallet”](#wallet) [`LocalWallet`](/konekt/api/konekt-ui/src/type-aliases/localwallet/) #### Returns [Section titled “Returns”](#returns) `void` *** ### connected [Section titled “connected”](#connected) > **connected**: `boolean` Whether this source currently has a connected wallet. Closes the modal when it turns true. *** ### wallets [Section titled “wallets”](#wallets) > **wallets**: readonly [`LocalWallet`](/konekt/api/konekt-ui/src/type-aliases/localwallet/)\[] Wallets this source discovered. # ModalProps > **ModalProps** = [`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/) & `object` Props for the accessible dialog shell used by konekt-ui. ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### children [Section titled “children”](#children) > **children**: `ReactNode` Dialog content. ### onClose [Section titled “onClose”](#onclose) > **onClose**: () => `void` Called for Escape, backdrop activation, and close controls supplied by the child content. #### Returns [Section titled “Returns”](#returns) `void` ### open [Section titled “open”](#open) > **open**: `boolean` Whether the dialog is rendered. ### title [Section titled “title”](#title) > **title**: `string` Accessible dialog label. Render the same title visibly in `children`. # Pairing > **Pairing** = `object` Connection state and actions consumed by [WalletModal](/konekt/api/konekt-ui/src/functions/walletmodal/). Use `useProviderPairing()` for a Konekt provider or `useWagmiPairing()` for wagmi instead of building this object by hand. ## Properties [Section titled “Properties”](#properties) ### chains? [Section titled “chains?”](#chains) > `optional` **chains?**: readonly `string`\[] CAIP-2 chain IDs known by the binding. The modal’s `chains` prop overrides them. *** ### connected [Section titled “connected”](#connected) > **connected**: `boolean` Whether a wallet is currently connected. *** ### connectLocal [Section titled “connectLocal”](#connectlocal) > **connectLocal**: (`wallet`) => `void` Connects one of the local wallets. #### Parameters [Section titled “Parameters”](#parameters) ##### wallet [Section titled “wallet”](#wallet) [`LocalWallet`](/konekt/api/konekt-ui/src/type-aliases/localwallet/) #### Returns [Section titled “Returns”](#returns) `void` *** ### error? [Section titled “error?”](#error) > `optional` **error?**: `string` Human-readable pairing error to display in the modal. *** ### local [Section titled “local”](#local) > **local**: readonly [`LocalWallet`](/konekt/api/konekt-ui/src/type-aliases/localwallet/)\[] Wallets already available through registered browser connectors. *** ### projectId? [Section titled “projectId?”](#projectid) > `optional` **projectId?**: `string` WalletConnect Cloud project ID the modal uses for Wallet Explorer listings. The pairing hooks read it from the Konekt provider or connector, so the modal never needs it separately. *** ### reset [Section titled “reset”](#reset) > **reset**: () => `void` Clears connection errors before a new modal flow. #### Returns [Section titled “Returns”](#returns-1) `void` *** ### start [Section titled “start”](#start) > **start**: (`onUri`) => () => `void` Starts WalletConnect pairing and reports its URI. The returned teardown cancels or detaches it. #### Parameters [Section titled “Parameters”](#parameters-1) ##### onUri [Section titled “onUri”](#onuri) (`uri`) => `void` #### Returns [Section titled “Returns”](#returns-2) () => `void` # PairingProvider > **PairingProvider** = `object` Provider surface used by [useProviderPairing](/konekt/api/konekt-ui/src/functions/useproviderpairing/). A Konekt `Provider` satisfies this type without an adapter or wrapper. ## Properties [Section titled “Properties”](#properties) ### chains? [Section titled “chains?”](#chains) > `optional` **chains?**: readonly `object`\[] The chains this provider proposes. The modal lists wallets that support them. *** ### connect [Section titled “connect”](#connect) > **connect**: (`opts?`) => `Promise`<`unknown`> Starts a connection and accepts a signal for cancellation. #### Parameters [Section titled “Parameters”](#parameters) ##### opts? [Section titled “opts?”](#opts) ###### signal? [Section titled “signal?”](#signal) `AbortSignal` #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> *** ### connected [Section titled “connected”](#connected) > **connected**: `boolean` Whether the provider already has an approved session. *** ### off [Section titled “off”](#off) > **off**: (`event`, `listener`) => `void` Removes a pairing-URI listener. #### Parameters [Section titled “Parameters”](#parameters-1) ##### event [Section titled “event”](#event) `"display_uri"` ##### listener [Section titled “listener”](#listener) (`uri`) => `void` #### Returns [Section titled “Returns”](#returns-1) `void` *** ### on [Section titled “on”](#on) > **on**: (`event`, `listener`) => `void` Adds a pairing-URI listener. #### Parameters [Section titled “Parameters”](#parameters-2) ##### event [Section titled “event”](#event-1) `"display_uri"` ##### listener [Section titled “listener”](#listener-1) (`uri`) => `void` #### Returns [Section titled “Returns”](#returns-2) `void` *** ### projectId? [Section titled “projectId?”](#projectid) > `optional` **projectId?**: `string` WalletConnect Cloud project ID, forwarded to the modal for Wallet Explorer listings. # ProviderPairingOptions > **ProviderPairingOptions** = `object` Options for [useProviderPairing](/konekt/api/konekt-ui/src/functions/useproviderpairing/). ## Properties [Section titled “Properties”](#properties) ### sources? [Section titled “sources?”](#sources) > `optional` **sources?**: readonly [`LocalWalletSource`](/konekt/api/konekt-ui/src/type-aliases/localwalletsource/)\[] Sources whose wallets appear as “Installed” choices next to WalletConnect pairing. # QrCodeProps > **QrCodeProps** = `Pick`<[`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/), `"className"` | `"style"` | `"unstyled"`> & `object` Props for the WalletConnect QR renderer. ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### size? [Section titled “size?”](#size) > `optional` **size?**: `number` Rendered width and height in CSS pixels. Defaults to 232. ### value [Section titled “value”](#value) > **value**: `string` Exact text encoded in the QR code, usually a `wc:` pairing URI. # WalletFilter > **WalletFilter** = `object` WalletConnect Explorer filters accepted by [WalletModal](/konekt/api/konekt-ui/src/functions/walletmodal/). ## Properties [Section titled “Properties”](#properties) ### exclude? [Section titled “exclude?”](#exclude) > `optional` **exclude?**: readonly `string`\[] Explorer IDs removed from each page after it is returned. *** ### featured? [Section titled “featured?”](#featured) > `optional` **featured?**: readonly `string`\[] Explorer IDs listed on the first screen. Defaults to [FEATURED\_WALLET\_IDS](/konekt/api/konekt-ui/src/variables/featured_wallet_ids/). *** ### include? [Section titled “include?”](#include) > `optional` **include?**: readonly `string`\[] When set, only these Explorer IDs are listed. # WalletLinks > **WalletLinks** = `object` Native-scheme and HTTPS universal links advertised for one platform. ## Properties [Section titled “Properties”](#properties) ### native [Section titled “native”](#native) > **native**: `string` Custom-scheme link, for example `metamask://`. Empty when unavailable. *** ### universal [Section titled “universal”](#universal) > **universal**: `string` HTTPS universal link. Empty when unavailable. # WalletModalProps > **WalletModalProps** = [`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/) & `object` Props for [WalletModal](/konekt/api/konekt-ui/src/functions/walletmodal/). ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### chains? [Section titled “chains?”](#chains) > `optional` **chains?**: readonly `string`\[] CAIP-2 chain IDs. Explorer results must support at least one. Defaults to `pairing.chains`. ### onClose [Section titled “onClose”](#onclose) > **onClose**: () => `void` Requests that the controlling component set `open` to `false`. #### Returns [Section titled “Returns”](#returns) `void` ### onDismiss? [Section titled “onDismiss?”](#ondismiss) > `optional` **onDismiss?**: () => `void` Runs when an unfinished pairing attempt is discarded: the user left, or the modal replaced a pairing that was about to lapse. Use it to cancel work owned outside the `Pairing`, such as a wagmi connector’s pending connection. #### Returns [Section titled “Returns”](#returns-1) `void` ### open [Section titled “open”](#open) > **open**: `boolean` Whether the dialog is rendered. ### pairing [Section titled “pairing”](#pairing) > **pairing**: [`Pairing`](/konekt/api/konekt-ui/src/type-aliases/pairing/) Connection binding created by `useProviderPairing()` or `useWagmiPairing()`. ### wallets? [Section titled “wallets?”](#wallets) > `optional` **wallets?**: [`WalletFilter`](/konekt/api/konekt-ui/src/type-aliases/walletfilter/) Include, exclude, and featured lists of WalletConnect Explorer IDs. # WcAppearanceProps > **WcAppearanceProps** = `object` Appearance options shared by konekt-ui components. ## Properties [Section titled “Properties”](#properties) ### className? [Section titled “className?”](#classname) > `optional` **className?**: `string` Additional class applied to the component root. *** ### style? [Section titled “style?”](#style) > `optional` **style?**: [`WcStyle`](/konekt/api/konekt-ui/src/type-aliases/wcstyle/) Inline styles and `--kui-*` design-token overrides applied to the component root. *** ### theme? [Section titled “theme?”](#theme) > `optional` **theme?**: [`WcTheme`](/konekt/api/konekt-ui/src/type-aliases/wctheme/) Color scheme. Defaults to `"system"`. *** ### unstyled? [Section titled “unstyled?”](#unstyled) > `optional` **unstyled?**: `boolean` Removes the default `kui-*` classes while preserving semantic `data-kui-*` attributes. # WcStyle > **WcStyle** = `CSSProperties` & `object` React inline styles plus konekt-ui custom properties such as `--kui-accent`. # WcTheme > **WcTheme** = `"light"` | `"dark"` | `"system"` Color scheme used by konekt-ui components. `"system"` follows the user’s OS preference. # EXPLORER_URL > `const` **EXPLORER\_URL**: `"https://explorer-api.walletconnect.com"` = `"https://explorer-api.walletconnect.com"` Base URL of WalletConnect’s public wallet listing API. # FEATURED_WALLET_IDS > `const` **FEATURED\_WALLET\_IDS**: readonly \[`"c57ca95b47569778a828d19178114f4db188b89b763c899ba0be274e97267d96"`, `"1ae92b26df02f0abca6304df07debccd18262fdf5fe82daa81593582dac9a369"`, `"4622a2b2d6af1c9844944291e5e7351a6aa24cd7b23099efac1b2fd875da31a0"`] Default WalletConnect Explorer IDs shown on the first modal screen. # abortPairing > **abortPairing**(): `void` Aborts the pending pairing proposal. Pass it as `onDismiss` so closing the modal stops the connect. ## Returns [Section titled “Returns”](#returns) `void` # AccountModal > **AccountModal**(`__namedParameters`): `Element` Displays the active wagmi account or configured EVM networks. The account view can copy the address and disconnect. The network view calls wagmi’s `switchChain` action while keeping the wallet connected. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`AccountModalProps`](/konekt/api/konekt-ui/src/wagmi/type-aliases/accountmodalprops/) ## Returns [Section titled “Returns”](#returns) `Element` # ConnectButton > **ConnectButton**(`__namedParameters`): `Element` Complete wagmi wallet control with connection, account, network, and disconnect dialogs. A wagmi connector whose `id` or `type` is `"konekt"` supplies WalletConnect pairing. Other configured connectors appear as installed wallet options. Pass `getWalletConnect` to register the Konekt connector only when the user starts pairing. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`ConnectButtonProps`](/konekt/api/konekt-ui/src/wagmi/type-aliases/connectbuttonprops/) ## Returns [Section titled “Returns”](#returns) `Element` # konekt > **konekt**(`parameters`): `CreateConnectorFn`<`EvmProvider`, `KonektConnectorProperties`> The Konekt connector for `createConfig()`. It imports Konekt and calls `Provider.init()` lazily when wagmi first asks for the provider, so static registration costs one dynamic import during wagmi’s reconnect and opens a relay socket only when a saved session exists. EVM chains come from the wagmi config; `display_uri` surfaces through the connector’s `message` event, which is how `ConnectButton` and `useWagmiPairing` find it. ## Parameters [Section titled “Parameters”](#parameters) ### parameters [Section titled “parameters”](#parameters-1) [`KonektParameters`](/konekt/api/konekt-ui/src/wagmi/type-aliases/konektparameters/) ## Returns [Section titled “Returns”](#returns) `CreateConnectorFn`<`EvmProvider`, `KonektConnectorProperties`> # useWagmiPairing > **useWagmiPairing**(`__namedParameters?`): [`Pairing`](/konekt/api/konekt-ui/src/type-aliases/pairing/) Creates a [Pairing](/konekt/api/konekt-ui/src/type-aliases/pairing/) from the nearest wagmi provider. Connectors other than Konekt become local wallet choices, injected ones only while their provider is in the browser. A connector whose `id` or `type` is `"konekt"` starts WalletConnect pairing and supplies `display_uri` through its message emitter. If no such connector is registered, pass `getWalletConnect` to create it lazily. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters? [Section titled “\_\_namedParameters?”](#__namedparameters) [`WagmiPairingOptions`](/konekt/api/konekt-ui/src/wagmi/type-aliases/wagmipairingoptions/) = `{}` ## Returns [Section titled “Returns”](#returns) [`Pairing`](/konekt/api/konekt-ui/src/type-aliases/pairing/) # AccountModalProps > **AccountModalProps** = [`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/) & `object` Props for the wagmi account and network dialog. ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### onClose [Section titled “onClose”](#onclose) > **onClose**: () => `void` Requests that the controlling component close the dialog. #### Returns [Section titled “Returns”](#returns) `void` ### onView [Section titled “onView”](#onview) > **onView**: (`view`) => `void` Requests a switch between account details and the network list. #### Parameters [Section titled “Parameters”](#parameters) ##### view [Section titled “view”](#view) `"account"` | `"networks"` #### Returns [Section titled “Returns”](#returns-1) `void` ### open [Section titled “open”](#open) > **open**: `boolean` Whether the dialog is rendered. ### view [Section titled “view”](#view-1) > **view**: `"account"` | `"networks"` Content shown when the dialog opens. # ConnectButtonProps > **ConnectButtonProps** = [`WcAppearanceProps`](/konekt/api/konekt-ui/src/type-aliases/wcappearanceprops/) & `object` Props for [ConnectButton](/konekt/api/konekt-ui/src/wagmi/functions/connectbutton/). ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### chains? [Section titled “chains?”](#chains) > `optional` **chains?**: readonly `string`\[] CAIP-2 chain IDs used to filter Explorer wallets. Defaults to configured wagmi chains. ### getWalletConnect? [Section titled “getWalletConnect?”](#getwalletconnect) > `optional` **getWalletConnect?**: () => `Promise`<`Connector`> Lazily registers and returns the Konekt wagmi connector when the config does not already contain one. #### Returns [Section titled “Returns”](#returns) `Promise`<`Connector`> ### onDismiss? [Section titled “onDismiss?”](#ondismiss) > `optional` **onDismiss?**: () => `void` Cancels pending connection work owned by the connector when the user dismisses pairing. #### Returns [Section titled “Returns”](#returns-1) `void` ### projectId? [Section titled “projectId?”](#projectid) > `optional` **projectId?**: `string` Project ID for Wallet Explorer listings, needed only with `getWalletConnect`: a registered Konekt connector already carries its own. ### wallets? [Section titled “wallets?”](#wallets) > `optional` **wallets?**: [`WalletFilter`](/konekt/api/konekt-ui/src/type-aliases/walletfilter/) Include, exclude, and featured lists of WalletConnect Explorer IDs. # KonektParameters > **KonektParameters** = `Pick`<`CreateProviderOptions`, `"projectId"` | `"metadata"` | `"relayUrl"`> Provider options the connector forwards to `Provider.init()`. Chains come from the wagmi config. # WagmiPairingOptions > **WagmiPairingOptions** = `object` Options for [useWagmiPairing](/konekt/api/konekt-ui/src/wagmi/functions/usewagmipairing/). ## Properties [Section titled “Properties”](#properties) ### getWalletConnect? [Section titled “getWalletConnect?”](#getwalletconnect) > `optional` **getWalletConnect?**: () => `Promise`<`Connector`> Registers and returns the Konekt connector on demand when the wagmi config does not already contain one. #### Returns [Section titled “Returns”](#returns) `Promise`<`Connector`> *** ### projectId? [Section titled “projectId?”](#projectid) > `optional` **projectId?**: `string` Project ID for Wallet Explorer listings when no Konekt connector is registered at the time the modal opens, which happens with `getWalletConnect`. A registered connector supplies its own. # bitcoin > **bitcoin**(`ref`): [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Creates one Bitcoin `Chain` for Provider configuration. Pass a CAIP-2 reference (usually the genesis block hash prefix) or a network definition with a string `id`, such as AppKit’s Bitcoin networks. ## Parameters [Section titled “Parameters”](#parameters) ### ref [Section titled “ref”](#ref) `string` | { `id`: `string`; } ## Returns [Section titled “Returns”](#returns) [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) # bitcoinAdapter > `const` **bitcoinAdapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/) = `adapter` Shared adapter for chains created by [bitcoin](/konekt/api/konekt/src/chains/bip122/functions/bitcoin/). # bitcoinMainnet > `const` **bitcoinMainnet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Bitcoin mainnet. # bitcoinSignet > `const` **bitcoinSignet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Bitcoin signet. # bitcoinTestnet > `const` **bitcoinTestnet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Bitcoin testnet. # EVENTS > `const` **EVENTS**: readonly \[`"bip122_addressesChanged"`] Bitcoin session events proposed and forwarded by the built-in adapter. # METHODS > `const` **METHODS**: readonly \[`"sendTransfer"`, `"getAccountAddresses"`, `"signPsbt"`, `"signMessage"`, `"bip122_signTransaction"`] Bitcoin methods proposed and forwarded by the built-in adapter. # cosmos > **cosmos**(`ref`): [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Creates one Cosmos `Chain` for Provider configuration. Pass a CAIP-2 reference (a network name such as `"cosmoshub-4"`) or a network definition with a string `id`. ## Parameters [Section titled “Parameters”](#parameters) ### ref [Section titled “ref”](#ref) `string` | { `id`: `string`; } ## Returns [Section titled “Returns”](#returns) [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) # cosmosAdapter > `const` **cosmosAdapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/) = `adapter` Shared adapter for chains created by [cosmos](/konekt/api/konekt/src/chains/cosmos/functions/cosmos/). # cosmoshub > `const` **cosmoshub**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Cosmos Hub mainnet (`cosmos:cosmoshub-4`). # METHODS > `const` **METHODS**: readonly \[`"cosmos_getAccounts"`, `"cosmos_signDirect"`, `"cosmos_signAmino"`] Cosmos methods proposed and forwarded by the built-in adapter. `cosmos_getAccounts` goes to the wallet because its result includes `algo` and `pubkey`, which a CAIP-10 session account does not contain. # osmosis > `const` **osmosis**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Osmosis mainnet (`cosmos:osmosis-1`). # evm > **evm**(`id`, `opts?`): [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Creates one EVM `Chain` for Provider configuration. Pass a decimal chain ID or a chain definition from viem, wagmi, or AppKit (see [ChainDefinition](/konekt/api/konekt/src/chains/eip155/type-aliases/chaindefinition/)). A definition’s first default HTTP RPC URL becomes the chain’s read transport; an explicit `read` in the options overrides it. Bare IDs never get an implicit transport. Each call creates one chain, so two networks with different RPC URLs are two calls. Do not pass bare numbers to `Provider`’s `chains` option. ## Parameters [Section titled “Parameters”](#parameters) ### id [Section titled “id”](#id) `number` | [`ChainDefinition`](/konekt/api/konekt/src/chains/eip155/type-aliases/chaindefinition/) ### opts? [Section titled “opts?”](#opts) [`EvmOpts`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmopts/) ## Returns [Section titled “Returns”](#returns) [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) ## Examples [Section titled “Examples”](#examples) **One network without JSON-RPC reads** ```ts chains: [evm(1)] ``` **Two networks with different read transports** ```ts chains: [ evm(1, { read: http(ethereumRpcUrl) }), evm(8453, { read: http(baseRpcUrl) }), ] ``` **viem or wagmi definitions, reads served by each chain’s public RPC** ```ts import { mainnet, base } from "viem/chains"; chains: [evm(mainnet), evm(base)] ``` # parseAccounts > **parseAccounts**(`session`): `object` Extracts EVM state from an approved session. The first approved EVM account determines the chain. Duplicate addresses across approved EVM chains are returned once. This is the wallet’s view of the session, which may name chains the provider was never configured with; the adapter picks its active chain with selectableChainIds. ## Parameters [Section titled “Parameters”](#parameters) ### session [Section titled “session”](#session) { `namespaces`: `Record`<`string`, `Namespace`>; } | `undefined` ## Returns [Section titled “Returns”](#returns) `object` ### accounts [Section titled “accounts”](#accounts) > **accounts**: `string`\[] ### chainId [Section titled “chainId”](#chainid) > **chainId**: `number` # parseSwitchChainId > **parseSwitchChainId**(`params`): `number` | `undefined` Reads the target chain from `wallet_switchEthereumChain` parameters. ## Parameters [Section titled “Parameters”](#parameters) ### params [Section titled “params”](#params) `unknown` ## Returns [Section titled “Returns”](#returns) `number` | `undefined` The decimal chain ID, or `undefined` when the parameters are malformed. # routeMethod > **routeMethod**(`method`): [`MethodRoute`](/konekt/api/konekt/src/chains/eip155/type-aliases/methodroute/) Classifies an EVM method after local methods have been handled. Known signing, transaction, and chain-switching methods go to the wallet. Other `eth_*`, `net_*`, and `web3_*` methods use the chain’s read transport. Everything else is unknown. ## Parameters [Section titled “Parameters”](#parameters) ### method [Section titled “method”](#method) `string` ## Returns [Section titled “Returns”](#returns) [`MethodRoute`](/konekt/api/konekt/src/chains/eip155/type-aliases/methodroute/) # toHexChain > **toHexChain**(`id`): `` `0x${string}` `` Converts a decimal EVM chain ID to the hexadecimal form required by EIP-1193. ## Parameters [Section titled “Parameters”](#parameters) ### id [Section titled “id”](#id) `number` ## Returns [Section titled “Returns”](#returns) `` `0x${string}` `` # ChainDefinition > **ChainDefinition** = `object` The subset of a viem, wagmi, or AppKit chain definition that [evm](/konekt/api/konekt/src/chains/eip155/functions/evm/) reads. Satisfied structurally by `viem/chains` entries, wagmi’s `config.chains`, and AppKit EVM networks, so those objects can be passed straight to `evm()` without a konekt dependency on the package they came from. ## Properties [Section titled “Properties”](#properties) ### id [Section titled “id”](#id) > **id**: `number` Decimal EVM chain ID. *** ### rpcUrls? [Section titled “rpcUrls?”](#rpcurls) > `optional` **rpcUrls?**: `object` RPC endpoints. The first default HTTP URL becomes the chain’s read transport. #### default? [Section titled “default?”](#default) > `optional` **default?**: `object` ##### default.http? [Section titled “default.http?”](#defaulthttp) > `optional` **http?**: readonly `string`\[] # EvmChain > **EvmChain** = [`Chain`](/konekt/api/konekt/src/type-aliases/chain/)<[`EvmExt`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmext/)> An EVM chain that adds [EvmExt](/konekt/api/konekt/src/chains/eip155/type-aliases/evmext/) properties to its provider. # EvmExt > **EvmExt** = `object` Properties added to a provider when at least one EVM chain is configured. ## Properties [Section titled “Properties”](#properties) ### accounts [Section titled “accounts”](#accounts) > **accounts**: `string`\[] Unique EVM addresses approved in the current session on configured chains. *** ### chainId [Section titled “chainId”](#chainid) > **chainId**: `number` Active decimal EVM chain ID. Always one of the configured chains. # EvmOpts > **EvmOpts** = `object` Optional behavior for the chain returned from [evm](/konekt/api/konekt/src/chains/eip155/functions/evm/). ## Properties [Section titled “Properties”](#properties) ### read? [Section titled “read?”](#read) > `optional` **read?**: (`req`) => `Promise`<`unknown`> JSON-RPC transport for `eth_*`, `net_*`, and `web3_*` reads after wallet methods are routed. `http(url)` from `konekt/http` is the standard transport. #### Parameters [Section titled “Parameters”](#parameters) ##### req [Section titled “req”](#req) [`RequestArguments`](/konekt/api/konekt/src/type-aliases/requestarguments/) #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> # MethodRoute > **MethodRoute** = `"wallet"` | `"rpc"` | `"unknown"` Destination selected for an EVM method. # arbitrumMainnet > `const` **arbitrumMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Arbitrum One mainnet (`eip155:42161`). # arbitrumSepolia > `const` **arbitrumSepolia**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Arbitrum Sepolia testnet (`eip155:421614`). # baseMainnet > `const` **baseMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Base mainnet (`eip155:8453`). # baseSepolia > `const` **baseSepolia**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Base Sepolia testnet (`eip155:84532`). # bscMainnet > `const` **bscMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) BNB Smart Chain mainnet (`eip155:56`). # bscTestnet > `const` **bscTestnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) BNB Smart Chain testnet (`eip155:97`). # ethereumMainnet > `const` **ethereumMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Ethereum mainnet (`eip155:1`). No read transport; use [evm](/konekt/api/konekt/src/chains/eip155/functions/evm/) with a definition or `read` for JSON-RPC reads. # ethereumSepolia > `const` **ethereumSepolia**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Ethereum Sepolia testnet (`eip155:11155111`). # EVENTS > `const` **EVENTS**: readonly \[`"chainChanged"`, `"accountsChanged"`] EVM session events proposed to the wallet by the built-in adapter. # evmAdapter > `const` **evmAdapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/)<[`EvmExt`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmext/)> Shared EVM adapter used by chains returned from [evm](/konekt/api/konekt/src/chains/eip155/functions/evm/). # METHODS > `const` **METHODS**: readonly \[`"eth_sendTransaction"`, `"personal_sign"`, `"eth_sign"`, `"eth_signTransaction"`, `"eth_signTypedData"`, `"eth_signTypedData_v3"`, `"eth_signTypedData_v4"`, `"eth_accounts"`, `"eth_requestAccounts"`, `"wallet_switchEthereumChain"`] EVM methods proposed to the wallet by the built-in adapter. # optimismMainnet > `const` **optimismMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) OP Mainnet (`eip155:10`). # optimismSepolia > `const` **optimismSepolia**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) OP Sepolia testnet (`eip155:11155420`). # polygonAmoy > `const` **polygonAmoy**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Polygon Amoy testnet (`eip155:80002`). # polygonMainnet > `const` **polygonMainnet**: [`EvmChain`](/konekt/api/konekt/src/chains/eip155/type-aliases/evmchain/) Polygon mainnet (`eip155:137`). # routes > `const` **routes**: `object` Routing rules used after the adapter handles its local account and chain methods. ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### rpc [Section titled “rpc”](#rpc) > `readonly` **rpc**: `RegExp` ### wallet [Section titled “wallet”](#wallet) > `readonly` **wallet**: `Set`<`string`> # forwardingNamespace > **forwardingNamespace**(`__namedParameters`): [`ForwardingNamespace`](/konekt/api/konekt/src/chains/generic/type-aliases/forwardingnamespace/) Creates an adapter and chain factory for a forwarding-only namespace. Every declared method goes to the wallet on the explicitly targeted, active, or first configured chain. Declared session events are emitted as EIP-1193 `message` events with `{ type, data }`. ## Parameters [Section titled “Parameters”](#parameters) ### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters) [`ForwardingNamespaceOptions`](/konekt/api/konekt/src/chains/generic/type-aliases/forwardingnamespaceoptions/) ## Returns [Section titled “Returns”](#returns) [`ForwardingNamespace`](/konekt/api/konekt/src/chains/generic/type-aliases/forwardingnamespace/) ## Example [Section titled “Example”](#example) ```ts const { chain } = forwardingNamespace({ namespace: "example", methods: ["example_signMessage"], events: ["example_accountsChanged"], }); const exampleMainnet = chain("mainnet"); ``` # ForwardingNamespace > **ForwardingNamespace** = `object` Adapter and chain factory returned by [forwardingNamespace](/konekt/api/konekt/src/chains/generic/functions/forwardingnamespace/). ## Properties [Section titled “Properties”](#properties) ### adapter [Section titled “adapter”](#adapter) > **adapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/) Shared adapter to use for every chain in this namespace. *** ### chain [Section titled “chain”](#chain) > **chain**: (`reference`) => [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Creates a chain from the namespace-specific CAIP-2 reference. #### Parameters [Section titled “Parameters”](#parameters) ##### reference [Section titled “reference”](#reference) `string` #### Returns [Section titled “Returns”](#returns) [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) # ForwardingNamespaceOptions > **ForwardingNamespaceOptions** = `object` Description of a WalletConnect namespace whose supported methods all go to the wallet. ## Properties [Section titled “Properties”](#properties) ### events? [Section titled “events?”](#events) > `optional` **events?**: readonly `string`\[] Session events to propose and expose through the provider’s `message` event. *** ### methods [Section titled “methods”](#methods) > **methods**: readonly `string`\[] JSON-RPC methods to propose and forward. *** ### namespace [Section titled “namespace”](#namespace) > **namespace**: `string` CAIP-2 namespace, without a chain reference or colon. # solana > **solana**(`ref`): [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Creates one Solana `Chain` for Provider configuration. Pass a CAIP-2 reference (usually the network’s genesis hash) or a network definition with a string `id`, such as AppKit’s Solana networks. ## Parameters [Section titled “Parameters”](#parameters) ### ref [Section titled “ref”](#ref) `string` | { `id`: `string`; } ## Returns [Section titled “Returns”](#returns) [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) # METHODS > `const` **METHODS**: readonly \[`"solana_getAccounts"`, `"solana_requestAccounts"`, `"solana_signMessage"`, `"solana_signTransaction"`, `"solana_signAllTransactions"`, `"solana_signAndSendTransaction"`] Solana methods proposed and forwarded by the built-in adapter. # solanaAdapter > `const` **solanaAdapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/) = `adapter` Shared adapter for chains created by [solana](/konekt/api/konekt/src/chains/solana/functions/solana/). # solanaDevnet > `const` **solanaDevnet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Solana devnet. # solanaMainnet > `const` **solanaMainnet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Solana mainnet-beta. # solanaTestnet > `const` **solanaTestnet**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) Solana testnet. # Provider An EIP-1193-compatible provider backed by a WalletConnect v2 session. Application code should call [Provider.init](/konekt/api/konekt/src/classes/provider/#init); tests that need an isolated or injected instance should call [Provider.create](/konekt/api/konekt/src/classes/provider/#create). ## Example [Section titled “Example”](#example) ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], }); provider.on("display_uri", showPairingUri); await provider.connect(); ``` ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new Provider**(`opts`, `deps?`): `Provider` Constructs a provider synchronously. Prefer [Provider.init](/konekt/api/konekt/src/classes/provider/#init) in applications and [Provider.create](/konekt/api/konekt/src/classes/provider/#create) in tests because those methods perform asynchronous seed loading and session restoration. #### Parameters [Section titled “Parameters”](#parameters) ##### opts [Section titled “opts”](#opts) [`CreateProviderOptions`](/konekt/api/konekt/src/type-aliases/createprovideroptions/) ##### deps? [Section titled “deps?”](#deps) [`ProviderDeps`](/konekt/api/konekt/src/type-aliases/providerdeps/) = `{}` #### Returns [Section titled “Returns”](#returns) `Provider` ## Properties [Section titled “Properties”](#properties) ### isWalletConnect [Section titled “isWalletConnect”](#iswalletconnect) > `readonly` **isWalletConnect**: `true` Allows provider consumers to identify this as a WalletConnect-backed provider. *** ### projectId [Section titled “projectId”](#projectid) > `readonly` **projectId**: `string` The WalletConnect Cloud project ID this provider was configured with. ## Accessors [Section titled “Accessors”](#accessors) ### accountsByChain [Section titled “accountsByChain”](#accountsbychain) #### Get Signature [Section titled “Get Signature”](#get-signature) > **get** **accountsByChain**(): `Record`<`string`, `string`\[]> Approved addresses grouped by CAIP-2 chain ID across all namespaces in the current session. ##### Example [Section titled “Example”](#example-1) ```ts { "eip155:1": ["0x…"], "cosmos:cosmoshub-4": ["cosmos1…"] } ``` ##### Returns [Section titled “Returns”](#returns-1) `Record`<`string`, `string`\[]> *** ### chains [Section titled “chains”](#chains) #### Get Signature [Section titled “Get Signature”](#get-signature-1) > **get** **chains**(): readonly [`Chain`](/konekt/api/konekt/src/type-aliases/chain/)\[] The configured chains this provider proposes, flattened into one list. This is configuration, not proof that the wallet approved every chain. Read [session](/konekt/api/konekt/src/classes/provider/#session) to inspect the approved namespaces. ##### Returns [Section titled “Returns”](#returns-2) readonly [`Chain`](/konekt/api/konekt/src/type-aliases/chain/)\[] *** ### connected [Section titled “connected”](#connected) #### Get Signature [Section titled “Get Signature”](#get-signature-2) > **get** **connected**(): `boolean` Whether the provider currently has an approved session. ##### Returns [Section titled “Returns”](#returns-3) `boolean` *** ### session [Section titled “session”](#session) #### Get Signature [Section titled “Get Signature”](#get-signature-3) > **get** **session**(): [`Session`](/konekt/api/konekt/src/type-aliases/session/) | `undefined` The approved session, or `undefined` before connection and after disconnection. ##### Returns [Section titled “Returns”](#returns-4) [`Session`](/konekt/api/konekt/src/type-aliases/session/) | `undefined` *** ### uri [Section titled “uri”](#uri) #### Get Signature [Section titled “Get Signature”](#get-signature-4) > **get** **uri**(): `string` | `undefined` The temporary pairing URI for the current proposal, when one exists. ##### Returns [Section titled “Returns”](#returns-5) `string` | `undefined` ## Methods [Section titled “Methods”](#methods) ### connect() [Section titled “connect()”](#connect) > **connect**(`__namedParameters?`): `Promise`<[`Session`](/konekt/api/konekt/src/type-aliases/session/)> Proposes a WalletConnect session and waits for the wallet to approve or reject it. Listen for `display_uri` before calling this method. Pass an `AbortSignal` so closing the pairing UI can cancel the attempt. If a feature rejects the settled session, this method disconnects it before rejecting. #### Parameters [Section titled “Parameters”](#parameters-1) ##### \_\_namedParameters? [Section titled “\_\_namedParameters?”](#__namedparameters) ###### signal? [Section titled “signal?”](#signal) `AbortSignal` #### Returns [Section titled “Returns”](#returns-6) `Promise`<[`Session`](/konekt/api/konekt/src/type-aliases/session/)> The approved WalletConnect session. *** ### disconnect() [Section titled “disconnect()”](#disconnect) > **disconnect**(): `Promise`<`void`> Ends the current session, clears adapter and feature state, and emits `disconnect`. #### Returns [Section titled “Returns”](#returns-7) `Promise`<`void`> *** ### enable() [Section titled “enable()”](#enable) > **enable**(): `Promise`<`string`\[]> Legacy EIP-1193 shortcut that connects if needed and returns the approved EVM accounts. New code can call [connect](/konekt/api/konekt/src/classes/provider/#connect) and then read the EVM adapter’s `accounts` property. #### Returns [Section titled “Returns”](#returns-8) `Promise`<`string`\[]> *** ### off() [Section titled “off()”](#off) > **off**<`K`>(`e`, `fn`): `void` Removes a listener previously passed to [on](/konekt/api/konekt/src/classes/provider/#on) or [once](/konekt/api/konekt/src/classes/provider/#once). #### Type Parameters [Section titled “Type Parameters”](#type-parameters) ##### K [Section titled “K”](#k) `K` *extends* keyof [`ProviderEvents`](/konekt/api/konekt/src/type-aliases/providerevents/) #### Parameters [Section titled “Parameters”](#parameters-2) ##### e [Section titled “e”](#e) `K` ##### fn [Section titled “fn”](#fn) (`p`) => `void` #### Returns [Section titled “Returns”](#returns-9) `void` *** ### on() [Section titled “on()”](#on) > **on**<`K`>(`e`, `fn`): `void` Adds an event listener that runs every time the event is emitted. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-1) ##### K [Section titled “K”](#k-1) `K` *extends* keyof [`ProviderEvents`](/konekt/api/konekt/src/type-aliases/providerevents/) #### Parameters [Section titled “Parameters”](#parameters-3) ##### e [Section titled “e”](#e-1) `K` ##### fn [Section titled “fn”](#fn-1) (`p`) => `void` #### Returns [Section titled “Returns”](#returns-10) `void` *** ### once() [Section titled “once()”](#once) > **once**<`K`>(`e`, `fn`): `void` Adds an event listener that removes itself after its first call. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-2) ##### K [Section titled “K”](#k-2) `K` *extends* keyof [`ProviderEvents`](/konekt/api/konekt/src/type-aliases/providerevents/) #### Parameters [Section titled “Parameters”](#parameters-4) ##### e [Section titled “e”](#e-2) `K` ##### fn [Section titled “fn”](#fn-2) (`p`) => `void` #### Returns [Section titled “Returns”](#returns-11) `void` *** ### removeListener() [Section titled “removeListener()”](#removelistener) > **removeListener**<`K`>(`e`, `fn`): `void` Alias for [off](/konekt/api/konekt/src/classes/provider/#off), provided for EIP-1193 and Node-style event compatibility. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-3) ##### K [Section titled “K”](#k-3) `K` *extends* keyof [`ProviderEvents`](/konekt/api/konekt/src/type-aliases/providerevents/) #### Parameters [Section titled “Parameters”](#parameters-5) ##### e [Section titled “e”](#e-3) `K` ##### fn [Section titled “fn”](#fn-3) (`p`) => `void` #### Returns [Section titled “Returns”](#returns-12) `void` *** ### request() [Section titled “request()”](#request) > **request**(`__namedParameters`, `chainId?`): `Promise`<{ } | `null`> Sends an EIP-1193 request through the adapter that supports its method. The optional `chainId` is a configured CAIP-2 ID such as `"eip155:8453"`. It targets this call only and does not change the active chain. Wallet methods require a connected session; EVM JSON-RPC reads require a `read` transport on the selected chain. #### Parameters [Section titled “Parameters”](#parameters-6) ##### \_\_namedParameters [Section titled “\_\_namedParameters”](#__namedparameters-1) [`RequestArguments`](/konekt/api/konekt/src/type-aliases/requestarguments/) ##### chainId? [Section titled “chainId?”](#chainid) `string` #### Returns [Section titled “Returns”](#returns-13) `Promise`<{ } | `null`> #### Throws [Section titled “Throws”](#throws) [ProviderRpcError](/konekt/api/konekt/src/classes/providerrpcerror/) with code 4100 when a wallet method has no session, 4200 when the method or read transport is unsupported, or -32602 for malformed parameters. *** ### create() [Section titled “create()”](#create) > `static` **create**<`C`>(`opts`, `deps?`): `Promise`<`Provider` & [`ChainExtensions`](/konekt/api/konekt/src/type-aliases/chainextensions/)<`C`>> Creates an independent provider, primarily for tests. Pass `deps` to replace the relay, session, seed, or storage. Supplying `deps.session` keeps the provider offline and does not open a real relay socket. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-4) ##### C [Section titled “C”](#c) `C` *extends* readonly [`ChainInput`](/konekt/api/konekt/src/type-aliases/chaininput/)\[] #### Parameters [Section titled “Parameters”](#parameters-7) ##### opts [Section titled “opts”](#opts-1) [`CreateProviderOptions`](/konekt/api/konekt/src/type-aliases/createprovideroptions/)<`C`> ##### deps? [Section titled “deps?”](#deps-1) [`ProviderDeps`](/konekt/api/konekt/src/type-aliases/providerdeps/) = `{}` #### Returns [Section titled “Returns”](#returns-14) `Promise`<`Provider` & [`ChainExtensions`](/konekt/api/konekt/src/type-aliases/chainextensions/)<`C`>> A new provider, extended with properties supplied by the configured adapters. *** ### init() [Section titled “init()”](#init) > `static` **init**<`C`>(`opts`): `Promise`<`Provider` & [`ChainExtensions`](/konekt/api/konekt/src/type-aliases/chainextensions/)<`C`>> Creates or returns the application-wide provider. The first call fixes the provider options; later calls return the same promise. Konekt uses the default relay and persistent browser storage unless the options override them. Register `display_uri` and `request_sent` listeners before starting work that emits those events. #### Type Parameters [Section titled “Type Parameters”](#type-parameters-5) ##### C [Section titled “C”](#c-1) `C` *extends* readonly [`ChainInput`](/konekt/api/konekt/src/type-aliases/chaininput/)\[] #### Parameters [Section titled “Parameters”](#parameters-8) ##### opts [Section titled “opts”](#opts-2) [`CreateProviderOptions`](/konekt/api/konekt/src/type-aliases/createprovideroptions/)<`C`> #### Returns [Section titled “Returns”](#returns-15) `Promise`<`Provider` & [`ChainExtensions`](/konekt/api/konekt/src/type-aliases/chainextensions/)<`C`>> The shared provider, extended with properties supplied by the configured adapters. # ProviderRpcError Error with a JSON-RPC or EIP-1193 numeric `code`. ## Extends [Section titled “Extends”](#extends) * `Error` ## Constructors [Section titled “Constructors”](#constructors) ### Constructor [Section titled “Constructor”](#constructor) > **new ProviderRpcError**(`code`, `message`): `ProviderRpcError` Creates a provider error with a machine-readable code and human-readable message. #### Parameters [Section titled “Parameters”](#parameters) ##### code [Section titled “code”](#code) `number` ##### message [Section titled “message”](#message) `string` #### Returns [Section titled “Returns”](#returns) `ProviderRpcError` #### Overrides [Section titled “Overrides”](#overrides) `Error.constructor` ## Properties [Section titled “Properties”](#properties) ### cause? [Section titled “cause?”](#cause) > `optional` **cause?**: `unknown` #### Inherited from [Section titled “Inherited from”](#inherited-from) `Error.cause` *** ### code [Section titled “code”](#code-1) > `readonly` **code**: `number` JSON-RPC or EIP-1193 error code. *** ### message [Section titled “message”](#message-1) > **message**: `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-1) `Error.message` *** ### name [Section titled “name”](#name) > **name**: `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-2) `Error.name` *** ### stack? [Section titled “stack?”](#stack) > `optional` **stack?**: `string` #### Inherited from [Section titled “Inherited from”](#inherited-from-3) `Error.stack` *** ### stackTraceLimit [Section titled “stackTraceLimit”](#stacktracelimit) > `static` **stackTraceLimit**: `number` The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured *after* the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. #### Inherited from [Section titled “Inherited from”](#inherited-from-4) `Error.stackTraceLimit` ## Methods [Section titled “Methods”](#methods) ### captureStackTrace() [Section titled “captureStackTrace()”](#capturestacktrace) > `static` **captureStackTrace**(`targetObject`, `constructorOpt?`): `void` Creates a `.stack` property on `targetObject`, which when accessed returns a string representing the location in the code at which `Error.captureStackTrace()` was called. ```js const myObject = {}; Error.captureStackTrace(myObject); myObject.stack; // Similar to `new Error().stack` ``` The first line of the trace will be prefixed with `${myObject.name}: ${myObject.message}`. The optional `constructorOpt` argument accepts a function. If given, all frames above `constructorOpt`, including `constructorOpt`, will be omitted from the generated stack trace. The `constructorOpt` argument is useful for hiding implementation details of error generation from the user. For instance: ```js function a() { b(); } function b() { c(); } function c() { // Create an error without stack trace to avoid calculating the stack trace twice. const { stackTraceLimit } = Error; Error.stackTraceLimit = 0; const error = new Error(); Error.stackTraceLimit = stackTraceLimit; // Capture the stack trace above function b Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace throw error; } a(); ``` #### Parameters [Section titled “Parameters”](#parameters-1) ##### targetObject [Section titled “targetObject”](#targetobject) `object` ##### constructorOpt? [Section titled “constructorOpt?”](#constructoropt) `Function` #### Returns [Section titled “Returns”](#returns-1) `void` #### Inherited from [Section titled “Inherited from”](#inherited-from-5) `Error.captureStackTrace` *** ### prepareStackTrace() [Section titled “prepareStackTrace()”](#preparestacktrace) > `static` **prepareStackTrace**(`err`, `stackTraces`): `any` #### Parameters [Section titled “Parameters”](#parameters-2) ##### err [Section titled “err”](#err) `Error` ##### stackTraces [Section titled “stackTraces”](#stacktraces) `CallSite`\[] #### Returns [Section titled “Returns”](#returns-2) `any` #### See [Section titled “See”](#see) #### Inherited from [Section titled “Inherited from”](#inherited-from-6) `Error.prepareStackTrace` # checkClaims > **checkClaims**(`payload`, `expected`): [`CacaoVerification`](/konekt/api/konekt/src/features/cacao/type-aliases/cacaoverification/) Checks that CACAO claims belong to the current authentication attempt. This compares the domain and nonce, optionally compares the audience URI, and enforces `exp` and `nbf` time limits. The server must issue and consume the nonce so it cannot be replayed. This function does not verify the signature. Authentication requires both this function and [verifyCacao](/konekt/api/konekt/src/features/cacao/functions/verifycacao/) to return `valid`. ## Parameters [Section titled “Parameters”](#parameters) ### payload [Section titled “payload”](#payload) [`CacaoPayload`](/konekt/api/konekt/src/type-aliases/cacaopayload/) ### expected [Section titled “expected”](#expected) [`ExpectedClaims`](/konekt/api/konekt/src/features/cacao/type-aliases/expectedclaims/) ## Returns [Section titled “Returns”](#returns) [`CacaoVerification`](/konekt/api/konekt/src/features/cacao/type-aliases/cacaoverification/) # formatCacaoMessage > **formatCacaoMessage**(`payload`, `iss?`): `string` Reconstructs the human-readable CAIP-122 message covered by a CACAO signature. The output is byte-compatible with the WalletConnect CAIP-122 formatter so EIP-191 address recovery and EIP-1271 contract verification use exactly the message shown to the wallet. ## Parameters [Section titled “Parameters”](#parameters) ### payload [Section titled “payload”](#payload) [`CacaoPayload`](/konekt/api/konekt/src/type-aliases/cacaopayload/) Claims returned by the wallet. ### iss? [Section titled “iss?”](#iss) `string` = `payload.iss` Issuer to format. Defaults to `payload.iss`. ## Returns [Section titled “Returns”](#returns) `string` ## Throws [Section titled “Throws”](#throws) When the issuer or audience is missing, the statement contains a line break, or the payload includes unsupported recap resources. # parseDidPkh > **parseDidPkh**(`iss`): [`DidPkh`](/konekt/api/konekt/src/features/cacao/type-aliases/didpkh/) | `undefined` Parses a `did:pkh` issuer such as `did:pkh:eip155:1:0xabc…`. ## Parameters [Section titled “Parameters”](#parameters) ### iss [Section titled “iss”](#iss) `string` ## Returns [Section titled “Returns”](#returns) [`DidPkh`](/konekt/api/konekt/src/features/cacao/type-aliases/didpkh/) | `undefined` The namespace, chain reference, and address, or `undefined` for a malformed issuer. # verifyCacao > **verifyCacao**(`cacao`, `opts?`): `Promise`<[`CacaoVerification`](/konekt/api/konekt/src/features/cacao/type-aliases/cacaoverification/)> Verifies the cryptographic signature on a CACAO. Call this on the server that makes the authentication decision, not in the browser. This function supports EIP-191 account signatures and EIP-1271 smart contract signatures. Signature verification does not check whether the domain, URI, nonce, or time limits match the current login attempt. Call [checkClaims](/konekt/api/konekt/src/features/cacao/functions/checkclaims/) as a separate required step. ## Parameters [Section titled “Parameters”](#parameters) ### cacao [Section titled “cacao”](#cacao) [`Cacao`](/konekt/api/konekt/src/type-aliases/cacao/) ### opts? [Section titled “opts?”](#opts) [`VerifyCacaoOptions`](/konekt/api/konekt/src/features/cacao/type-aliases/verifycacaooptions/) = `{}` ## Returns [Section titled “Returns”](#returns) `Promise`<[`CacaoVerification`](/konekt/api/konekt/src/features/cacao/type-aliases/cacaoverification/)> `valid` when the signature passes, `invalid` when it fails, or `unverifiable` when this process cannot complete the check. # CacaoVerification > **CacaoVerification** = { `status`: `"valid"`; } | { `reason`: `string`; `status`: `"invalid"`; } | { `reason`: `string`; `status`: `"unverifiable"`; } `unverifiable` is not `invalid`. A caller must not log someone in on it, and must not accuse the wallet of forging either; it means this process could not check, so a smart account with no RPC reads differently from a bad signature. # DidPkh > **DidPkh** = `object` Parsed `did:pkh` issuer identifying a chain account. ## Properties [Section titled “Properties”](#properties) ### address [Section titled “address”](#address) > **address**: `string` Namespace-specific account address. *** ### namespace [Section titled “namespace”](#namespace) > **namespace**: `string` CAIP-2 namespace, for example `"eip155"`. *** ### reference [Section titled “reference”](#reference) > **reference**: `string` Chain reference, for example `"1"`. # ExpectedClaims > **ExpectedClaims** = `object` Server-side values expected in a CACAO for the current authentication attempt. ## Properties [Section titled “Properties”](#properties) ### domain [Section titled “domain”](#domain) > **domain**: `string` Exact application host that issued the challenge. *** ### nonce [Section titled “nonce”](#nonce) > **nonce**: `string` Fresh, single-use nonce previously issued by the server. *** ### now? [Section titled “now?”](#now) > `optional` **now?**: `Date` Time used for expiration checks. Defaults to the current time; inject in deterministic tests. *** ### uri? [Section titled “uri?”](#uri) > `optional` **uri?**: `string` Expected audience, matched against `aud` and then the legacy `uri` field. # VerifyCacaoOptions > **VerifyCacaoOptions** = `object` Options for cryptographic CACAO verification. ## Properties [Section titled “Properties”](#properties) ### call? [Section titled “call?”](#call) > `optional` **call?**: (`req`) => `Promise`<`unknown`> JSON-RPC call function for the issuer’s chain. EIP-1271 smart contract signatures require it; `http(url)` from `konekt/http` has the expected shape. EIP-191 signatures do not use it. #### Parameters [Section titled “Parameters”](#parameters) ##### req [Section titled “req”](#req) [`RequestArguments`](/konekt/api/konekt/src/type-aliases/requestarguments/) #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> # cacaosOf > **cacaosOf**(`session`): [`Cacao`](/konekt/api/konekt/src/type-aliases/cacao/)\[] Reads the CACAOs returned for this session’s authentication request. ## Parameters [Section titled “Parameters”](#parameters) ### session [Section titled “session”](#session) [`Session`](/konekt/api/konekt/src/type-aliases/session/) | `undefined` ## Returns [Section titled “Returns”](#returns) [`Cacao`](/konekt/api/konekt/src/type-aliases/cacao/)\[] The response array, or an empty array when the session is absent or the wallet did not answer authentication. # siwe > **siwe**(`options`): [`Feature`](/konekt/api/konekt/src/type-aliases/feature/) Adds a CAIP-122 authentication request to the WalletConnect session proposal. The feature fetches a nonce in `onProposal`. After approval it checks that returned CACAOs match that nonce, the requested domain and URI, and an account granted by the session. It does not verify signatures. Send the returned CACAOs to the server that makes the authentication decision. That server must call both `verifyCacao()` and `checkClaims()` from `konekt/cacao`. ## Parameters [Section titled “Parameters”](#parameters) ### options [Section titled “options”](#options) [`SiweOptions`](/konekt/api/konekt/src/features/siwe/type-aliases/siweoptions/) ## Returns [Section titled “Returns”](#returns) [`Feature`](/konekt/api/konekt/src/type-aliases/feature/) ## Example [Section titled “Example”](#example) ```ts features: [ siwe({ domain: location.host, uri: location.origin, chains: ["eip155:1"], getNonce: () => fetch("/auth/nonce").then((response) => response.text()), }), ] ``` ## Throws [Section titled “Throws”](#throws) When `resources` contains an unsupported `urn:recap:` entry. # SiweOptions > **SiweOptions** = `object` Configuration for [siwe](/konekt/api/konekt/src/features/siwe/functions/siwe/) proposal authentication. ## Properties [Section titled “Properties”](#properties) ### chains [Section titled “chains”](#chains) > **chains**: `string`\[] CAIP-2 chain IDs to authenticate. The wallet returns one CACAO per account it signs for. *** ### domain [Section titled “domain”](#domain) > **domain**: `string` Site asking the user to sign in, exactly as the wallet should display it. Usually `location.host`. *** ### exp? [Section titled “exp?”](#exp) > `optional` **exp?**: `string` Optional ISO timestamp after which the authentication message is expired. *** ### getNonce [Section titled “getNonce”](#getnonce) > **getNonce**: () => `string` | `Promise`<`string`> Returns a fresh, single-use challenge. This is awaited for every connection attempt so the nonce can be issued by the server immediately before pairing. #### Returns [Section titled “Returns”](#returns) `string` | `Promise`<`string`> *** ### nbf? [Section titled “nbf?”](#nbf) > `optional` **nbf?**: `string` Optional ISO timestamp before which the authentication message is not valid. *** ### requestId? [Section titled “requestId?”](#requestid) > `optional` **requestId?**: `string` Optional application-specific identifier included in the signed message. *** ### required? [Section titled “required?”](#required) > `optional` **required?**: `boolean` Whether a wallet must answer the authentication request. Defaults to `true`. When `true`, a wallet that ignores authentication causes `connect()` to reject. Set this to `false` to allow a connected but signed-out session, then use `cacaosOf(session).length` to distinguish that state. *** ### resources? [Section titled “resources?”](#resources) > `optional` **resources?**: `string`\[] Optional resource URIs included in the signed message. `urn:recap:` resources are unsupported. *** ### statement? [Section titled “statement?”](#statement) > `optional` **statement?**: `string` Optional human-readable reason for signing in. Keep it to one line. *** ### uri [Section titled “uri”](#uri) > **uri**: `string` Application audience URI. Usually `location.origin`. # accountsByChain > **accountsByChain**(`namespaces`): `Record`<`string`, `string`\[]> Groups approved session addresses by CAIP-2 chain ID. Addresses stay separated by chain because some namespaces, including Cosmos, use a different address for each chain. ## Parameters [Section titled “Parameters”](#parameters) ### namespaces [Section titled “namespaces”](#namespaces) `Record`<`string`, `Namespace`> | `undefined` ## Returns [Section titled “Returns”](#returns) `Record`<`string`, `string`\[]> A new record. Missing namespaces produce an empty object. # formatWalletRedirect > **formatWalletRedirect**(`href`, `id`, `topic`): `string` Builds a wallet URL for a request on an existing session. Telegram Mini App URLs receive a base64url `startapp` payload. Other URLs receive a `/wc?requestId=…&sessionTopic=…` path. This is not a pairing deep-link formatter: it carries a session request ID and topic, not a `wc:` pairing URI. ## Parameters [Section titled “Parameters”](#parameters) ### href [Section titled “href”](#href) `string` Native or universal wallet URL. ### id [Section titled “id”](#id) `number` JSON-RPC ID of the published session request. ### topic [Section titled “topic”](#topic) `string` WalletConnect topic of the approved session. ## Returns [Section titled “Returns”](#returns) `string` # memoryStorage > **memoryStorage**(): [`Storage`](/konekt/api/konekt/src/type-aliases/storage/) Creates non-persistent storage backed by a new in-memory map. Each call returns an isolated store. Data disappears when the object is discarded. ## Returns [Section titled “Returns”](#returns) [`Storage`](/konekt/api/konekt/src/type-aliases/storage/) # parseCaipAccount > **parseCaipAccount**(`account`): [`CaipAccount`](/konekt/api/konekt/src/type-aliases/caipaccount/) | `undefined` Splits a CAIP-10 account into its CAIP-2 chain ID and address. ## Parameters [Section titled “Parameters”](#parameters) ### account [Section titled “account”](#account) `string` Account in `namespace:reference:address` form. ## Returns [Section titled “Returns”](#returns) [`CaipAccount`](/konekt/api/konekt/src/type-aliases/caipaccount/) | `undefined` The parsed account, or `undefined` when any required part is missing. ## Example [Section titled “Example”](#example) ```ts `parseCaipAccount("eip155:1:0xabc")` returns `{ chainId: "eip155:1", address: "0xabc" }`. ``` # resolveChainId > **resolveChainId**(`req`, `ctx`, `namespace`): `string` | `undefined` Selects the chain for a namespace. An explicitly targeted chain wins, followed by the active chain and then the first configured chain in the namespace. ## Parameters [Section titled “Parameters”](#parameters) ### req [Section titled “req”](#req) [`RpcRequest`](/konekt/api/konekt/src/type-aliases/rpcrequest/) ### ctx [Section titled “ctx”](#ctx) [`Ctx`](/konekt/api/konekt/src/type-aliases/ctx/) ### namespace [Section titled “namespace”](#namespace) `string` ## Returns [Section titled “Returns”](#returns) `string` | `undefined` A CAIP-2 chain ID, or `undefined` when the provider has no chain in that namespace. # http > **http**(`url`): (`__namedParameters`) => `Promise`<`unknown`> Creates a JSON-RPC HTTP transport for EVM read methods. Pass the result to `evm(id, { read })`. The URL must serve the same network as that EVM chain. This transport is used for routed `eth_*`, `net_*`, and `web3_*` reads; it is never a fallback for signing, transactions, or unknown methods. JSON-RPC error responses become [ProviderRpcError](/konekt/api/konekt/src/classes/providerrpcerror/) instances with the server’s code and message. ## Parameters [Section titled “Parameters”](#parameters) ### url [Section titled “url”](#url) `string` HTTP or HTTPS JSON-RPC endpoint. ## Returns [Section titled “Returns”](#returns) A call function compatible with `EvmOpts.read` and `VerifyCacaoOptions.call`. (`__namedParameters`) => `Promise`<`unknown`> ## Example [Section titled “Example”](#example) ```ts import { evm } from "konekt/eip155"; import { http } from "konekt/http"; const ethereum = evm(1, { read: http(ethereumRpcUrl) }); ``` # Cacao > **Cacao** = `object` A CAIP-74 Chain Agnostic CApability Object returned by proposal authentication. `p` is the signed payload. `s.t` identifies the signature scheme; Konekt verifies `eip191` and `eip1271`. ## Properties [Section titled “Properties”](#properties) ### h [Section titled “h”](#h) > **h**: `object` Header. CAIP-74 authentication responses use type `caip122`. #### t [Section titled “t”](#t) > **t**: `string` *** ### p [Section titled “p”](#p) > **p**: [`CacaoPayload`](/konekt/api/konekt/src/type-aliases/cacaopayload/) Claims and sign-in message fields covered by the signature. *** ### s [Section titled “s”](#s) > **s**: `object` Signature type, signature bytes, and optional reconstructed message. #### m? [Section titled “m?”](#m) > `optional` **m?**: `string` #### s [Section titled “s”](#s-1) > **s**: `string` #### t [Section titled “t”](#t-1) > **t**: `string` # CacaoPayload > **CacaoPayload** = `object` Claims signed into a CACAO authentication response. ## Properties [Section titled “Properties”](#properties) ### aud? [Section titled “aud?”](#aud) > `optional` **aud?**: `string` Audience URI used by proposal authentication. *** ### domain [Section titled “domain”](#domain) > **domain**: `string` Application host the wallet displayed as the party requesting authentication. *** ### exp? [Section titled “exp?”](#exp) > `optional` **exp?**: `string` Optional ISO timestamp after which the message is expired. *** ### iat [Section titled “iat”](#iat) > **iat**: `string` ISO timestamp for when the message was issued. *** ### iss [Section titled “iss”](#iss) > **iss**: `string` Signing account as a `did:pkh` identifier, for example `did:pkh:eip155:1:0xabc…`. *** ### nbf? [Section titled “nbf?”](#nbf) > `optional` **nbf?**: `string` Optional ISO timestamp before which the message is not valid. *** ### nonce [Section titled “nonce”](#nonce) > **nonce**: `string` Single-use challenge issued by the application server. *** ### requestId? [Section titled “requestId?”](#requestid) > `optional` **requestId?**: `string` Optional application-specific request identifier. *** ### resources? [Section titled “resources?”](#resources) > `optional` **resources?**: `string`\[] Optional resource URIs covered by the authentication message. *** ### statement? [Section titled “statement?”](#statement) > `optional` **statement?**: `string` Optional human-readable reason for signing in. *** ### uri? [Section titled “uri?”](#uri) > `optional` **uri?**: `string` Legacy audience field used when `aud` is absent. *** ### version [Section titled “version”](#version) > **version**: `string` Message format version, currently `"1"`. # CaipAccount > **CaipAccount** = `object` A parsed CAIP-10 account: its CAIP-2 chain ID and namespace-specific address. ## Properties [Section titled “Properties”](#properties) ### address [Section titled “address”](#address) > **address**: `string` *** ### chainId [Section titled “chainId”](#chainid) > **chainId**: `string` # Chain > **Chain**<`Ext`> = `object` One configured CAIP-2 chain and the adapter that handles its namespace. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### Ext [Section titled “Ext”](#ext) `Ext` = `object` ## Properties [Section titled “Properties”](#properties) ### adapter [Section titled “adapter”](#adapter) > **adapter**: [`ChainAdapter`](/konekt/api/konekt/src/type-aliases/chainadapter/)<`Ext`> Shared behavior for this chain’s namespace. *** ### id [Section titled “id”](#id) > **id**: `string` Complete CAIP-2 ID, such as `"eip155:1"`. *** ### namespace [Section titled “namespace”](#namespace) > **namespace**: `string` CAIP-2 namespace, such as `"eip155"`. *** ### read? [Section titled “read?”](#read) > `optional` **read?**: (`req`) => `Promise`<`unknown`> Optional transport for chain reads. The built-in EVM adapter uses it for JSON-RPC reads. #### Parameters [Section titled “Parameters”](#parameters) ##### req [Section titled “req”](#req) [`RequestArguments`](/konekt/api/konekt/src/type-aliases/requestarguments/) #### Returns [Section titled “Returns”](#returns) `Promise`<`unknown`> # ChainAdapter > **ChainAdapter**<`Ext`> = `object` Behavior shared by every configured chain in a namespace. `handle` returns `undefined` when the adapter does not own a method. Any other value, including `null`, is the final result. `extend` may add namespace-specific properties to the provider. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### Ext [Section titled “Ext”](#ext) `Ext` = `object` ## Properties [Section titled “Properties”](#properties) ### events [Section titled “events”](#events) > **events**: `string`\[] Wallet events proposed for this namespace. *** ### extend? [Section titled “extend?”](#extend) > `optional` **extend?**: (`ctx`) => `Ext` Adds namespace-specific getters or methods to the provider. #### Parameters [Section titled “Parameters”](#parameters) ##### ctx [Section titled “ctx”](#ctx) [`Ctx`](/konekt/api/konekt/src/type-aliases/ctx/) #### Returns [Section titled “Returns”](#returns) `Ext` *** ### handle? [Section titled “handle?”](#handle) > `optional` **handle?**: (`req`, `ctx`) => `Promise`<`unknown`> | `unknown` Handles a supported request, or returns `undefined` so another adapter may handle it. #### Parameters [Section titled “Parameters”](#parameters-1) ##### req [Section titled “req”](#req) [`RpcRequest`](/konekt/api/konekt/src/type-aliases/rpcrequest/) ##### ctx [Section titled “ctx”](#ctx-1) [`Ctx`](/konekt/api/konekt/src/type-aliases/ctx/) #### Returns [Section titled “Returns”](#returns-1) `Promise`<`unknown`> | `unknown` *** ### methods [Section titled “methods”](#methods) > **methods**: `string`\[] Wallet methods proposed for this namespace. *** ### namespace [Section titled “namespace”](#namespace) > **namespace**: `string` CAIP-2 namespace, for example `"eip155"` or `"solana"`. *** ### onDisconnect? [Section titled “onDisconnect?”](#ondisconnect) > `optional` **onDisconnect?**: () => `void` Clears adapter-owned state after disconnection. #### Returns [Section titled “Returns”](#returns-2) `void` *** ### onEvent? [Section titled “onEvent?”](#onevent) > `optional` **onEvent?**: (`name`, `data`, `chainId`, `ctx`) => `void` Maps a session event to adapter state and public provider events. #### Parameters [Section titled “Parameters”](#parameters-2) ##### name [Section titled “name”](#name) `string` ##### data [Section titled “data”](#data) `unknown` ##### chainId [Section titled “chainId”](#chainid) `string` | `undefined` ##### ctx [Section titled “ctx”](#ctx-2) [`Ctx`](/konekt/api/konekt/src/type-aliases/ctx/) #### Returns [Section titled “Returns”](#returns-3) `void` *** ### onSettle? [Section titled “onSettle?”](#onsettle) > `optional` **onSettle?**: (`session`, `ctx`) => `void` Updates adapter state after a session is approved or restored. #### Parameters [Section titled “Parameters”](#parameters-3) ##### session [Section titled “session”](#session) [`Session`](/konekt/api/konekt/src/type-aliases/session/) ##### ctx [Section titled “ctx”](#ctx-3) [`Ctx`](/konekt/api/konekt/src/type-aliases/ctx/) #### Returns [Section titled “Returns”](#returns-4) `void` # ChainExtensions > **ChainExtensions**<`C`> = `UnionToIntersection`<`AdapterExt`<`FlattenChains`<`C`>>> Type-level intersection of the provider extensions supplied by configured chain adapters. ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### C [Section titled “C”](#c) `C` *extends* readonly `unknown`\[] # ChainInput > **ChainInput** = [`Chain`](/konekt/api/konekt/src/type-aliases/chain/) | readonly [`Chain`](/konekt/api/konekt/src/type-aliases/chain/)\[] A single chain or one array of chains; the kernel flattens one level of nesting. # CreateProviderOptions > **CreateProviderOptions**<`C`> = `object` Options shared by [Provider.init](/konekt/api/konekt/src/classes/provider/#init) and [Provider.create](/konekt/api/konekt/src/classes/provider/#create). ## Type Parameters [Section titled “Type Parameters”](#type-parameters) ### C [Section titled “C”](#c) `C` *extends* readonly [`ChainInput`](/konekt/api/konekt/src/type-aliases/chaininput/)\[] = readonly [`ChainInput`](/konekt/api/konekt/src/type-aliases/chaininput/)\[] ## Properties [Section titled “Properties”](#properties) ### chains [Section titled “chains”](#chains) > **chains**: `C` Chain objects returned by factories such as `evm(1)` or named chains such as `solanaMainnet`, for example `[evm(1), evm(8453), solanaMainnet]`. Each factory call creates one chain. *** ### features? [Section titled “features?”](#features) > `optional` **features?**: [`Feature`](/konekt/api/konekt/src/type-aliases/feature/)\[] Optional proposal features such as `siwe()`. *** ### metadata [Section titled “metadata”](#metadata) > **metadata**: [`Metadata`](/konekt/api/konekt/src/type-aliases/metadata/) App name, description, URL, and icons shown by the wallet during approval. *** ### onDebug? [Section titled “onDebug?”](#ondebug) > `optional` **onDebug?**: [`OnDebug`](/konekt/api/konekt/src/type-aliases/ondebug/) Receives structured protocol diagnostics. Avoid logging secrets in production. *** ### projectId [Section titled “projectId”](#projectid) > **projectId**: `string` Project ID from WalletConnect Cloud. It authenticates this app to the relay. *** ### relayUrl? [Section titled “relayUrl?”](#relayurl) > `optional` **relayUrl?**: `string` WalletConnect relay WebSocket URL. Omit to use the public default. *** ### storage? [Section titled “storage?”](#storage) > `optional` **storage?**: [`Storage`](/konekt/api/konekt/src/type-aliases/storage/) | `null` Storage for the relay seed and settled session. Omit for `localStorage` in a browser or memory in Node.js. Pass `null` to disable persistence. *** ### ttl? [Section titled “ttl?”](#ttl) > `optional` **ttl?**: `Partial`<[`TtlConfig`](/konekt/api/konekt/src/type-aliases/ttlconfig/)> Protocol lifetimes in seconds. Omitted fields keep their values from [TTL](/konekt/api/konekt/src/variables/ttl/). # Ctx > **Ctx** = `object` Provider operations and state exposed to a chain adapter. ## Properties [Section titled “Properties”](#properties) ### activeChainId [Section titled “activeChainId”](#activechainid) > **activeChainId**: (`namespace`) => `string` | `undefined` Reads the active CAIP-2 chain ID for a namespace. #### Parameters [Section titled “Parameters”](#parameters) ##### namespace [Section titled “namespace”](#namespace) `string` #### Returns [Section titled “Returns”](#returns) `string` | `undefined` *** ### chains [Section titled “chains”](#chains) > **chains**: [`Chain`](/konekt/api/konekt/src/type-aliases/chain/)\[] All chains configured on the provider. *** ### emit [Section titled “emit”](#emit) > **emit**: (`event`, `payload`) => `void` Emits a public provider event. #### Parameters [Section titled “Parameters”](#parameters-1) ##### event [Section titled “event”](#event) `string` ##### payload [Section titled “payload”](#payload) `unknown` #### Returns [Section titled “Returns”](#returns-1) `void` *** ### forward [Section titled “forward”](#forward) > **forward**: (`req`) => `Promise`<`unknown`> Sends a fully targeted request through the WalletConnect session. #### Parameters [Section titled “Parameters”](#parameters-2) ##### req [Section titled “req”](#req) `ForwardedRequest` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`unknown`> *** ### session [Section titled “session”](#session) > **session**: () => [`Session`](/konekt/api/konekt/src/type-aliases/session/) | `undefined` Reads the current approved session. #### Returns [Section titled “Returns”](#returns-3) [`Session`](/konekt/api/konekt/src/type-aliases/session/) | `undefined` *** ### setActiveChainId [Section titled “setActiveChainId”](#setactivechainid) > **setActiveChainId**: (`namespace`, `id`) => `void` Replaces the active CAIP-2 chain ID for a namespace. #### Parameters [Section titled “Parameters”](#parameters-3) ##### namespace [Section titled “namespace”](#namespace-1) `string` ##### id [Section titled “id”](#id) `string` #### Returns [Section titled “Returns”](#returns-4) `void` # DebugEvent > **DebugEvent** = { `type`: `"socket_open"`; } | { `code`: `number`; `reason`: `string`; `type`: `"socket_close"`; } | { `tag?`: `number`; `topic`: `string`; `type`: `"publish"`; } | { `topic`: `string`; `type`: `"inbound"`; } | { `type`: `"settle"`; } | { `error`: `string`; `type`: `"error"`; } Structured provider diagnostic. Events report relay lifecycle and protocol progress without exposing encrypted payload contents. # Feature > **Feature** = `object` Optional hooks that add behavior to session setup without intercepting normal requests. ## Properties [Section titled “Properties”](#properties) ### name [Section titled “name”](#name) > **name**: `string` Stable feature name used for diagnostics. *** ### onDisconnect? [Section titled “onDisconnect?”](#ondisconnect) > `optional` **onDisconnect?**: () => `void` Clears feature-owned state after the session ends. #### Returns [Section titled “Returns”](#returns) `void` *** ### onProposal? [Section titled “onProposal?”](#onproposal) > `optional` **onProposal?**: (`p`) => [`Proposal`](/konekt/api/konekt/src/type-aliases/proposal/) | `undefined` | `Promise`<[`Proposal`](/konekt/api/konekt/src/type-aliases/proposal/) | `undefined`> Runs before the proposal is published. It may await work such as fetching a server nonce and may return a replacement proposal. #### Parameters [Section titled “Parameters”](#parameters) ##### p [Section titled “p”](#p) [`Proposal`](/konekt/api/konekt/src/type-aliases/proposal/) #### Returns [Section titled “Returns”](#returns-1) [`Proposal`](/konekt/api/konekt/src/type-aliases/proposal/) | `undefined` | `Promise`<[`Proposal`](/konekt/api/konekt/src/type-aliases/proposal/) | `undefined`> *** ### onSettle? [Section titled “onSettle?”](#onsettle) > `optional` **onSettle?**: (`s`) => `void` | `Promise`<`void`> Runs after approval. Throwing rejects `connect()` and tears the new session down. #### Parameters [Section titled “Parameters”](#parameters-1) ##### s [Section titled “s”](#s) [`Session`](/konekt/api/konekt/src/type-aliases/session/) #### Returns [Section titled “Returns”](#returns-2) `void` | `Promise`<`void`> # Hex > **Hex** = `` `0x${string}` `` A hexadecimal string with a `0x` prefix. # Metadata > **Metadata** = `object` Application details shown by the wallet when it asks the user to approve a session. ## Properties [Section titled “Properties”](#properties) ### description [Section titled “description”](#description) > **description**: `string` Short explanation of what the application does. *** ### icons [Section titled “icons”](#icons) > **icons**: `string`\[] Absolute image URLs the wallet may use as the application icon. *** ### name [Section titled “name”](#name) > **name**: `string` Human-readable application name. *** ### redirect? [Section titled “redirect?”](#redirect) > `optional` **redirect?**: `object` Optional native and universal return URLs advertised to the wallet. #### native? [Section titled “native?”](#native) > `optional` **native?**: `string` #### universal? [Section titled “universal?”](#universal) > `optional` **universal?**: `string` *** ### url [Section titled “url”](#url) > **url**: `string` Canonical application URL. # OnDebug > **OnDebug** = (`e`) => `void` Callback passed as `CreateProviderOptions.onDebug` to receive structured diagnostics. ## Parameters [Section titled “Parameters”](#parameters) ### e [Section titled “e”](#e) [`DebugEvent`](/konekt/api/konekt/src/type-aliases/debugevent/) ## Returns [Section titled “Returns”](#returns) `void` # Proposal > **Proposal** = `object` WalletConnect session proposal before it is published to the relay. ## Properties [Section titled “Properties”](#properties) ### expiryTimestamp [Section titled “expiryTimestamp”](#expirytimestamp) > **expiryTimestamp**: `number` Unix timestamp in seconds when the proposal expires. *** ### optionalNamespaces [Section titled “optionalNamespaces”](#optionalnamespaces) > **optionalNamespaces**: `Record`<`string`, { `chains`: `string`\[]; `events`: `string`\[]; `methods`: `string`\[]; }> Namespace capabilities the wallet may approve. Konekt puts configured chains here. *** ### proposer [Section titled “proposer”](#proposer) > **proposer**: `object` App identity and metadata presented to the wallet. #### metadata [Section titled “metadata”](#metadata) > **metadata**: [`Metadata`](/konekt/api/konekt/src/type-aliases/metadata/) #### publicKey [Section titled “publicKey”](#publickey) > **publicKey**: `string` *** ### relays [Section titled “relays”](#relays) > **relays**: `object`\[] Relay protocols supported by the proposing app. #### protocol [Section titled “protocol”](#protocol) > **protocol**: `string` *** ### requests? [Section titled “requests?”](#requests) > `optional` **requests?**: `Record`<`string`, `unknown`> Feature-owned side requests. A feature writes its own key and reads the matching key from `Session.proposalRequestsResponses`. *** ### requiredNamespaces [Section titled “requiredNamespaces”](#requirednamespaces) > **requiredNamespaces**: `Record`<`string`, { `chains?`: `string`\[]; `events`: `string`\[]; `methods`: `string`\[]; }> Namespace capabilities the wallet must support. # ProposalRequestsResponses > **ProposalRequestsResponses** = `object` What the wallet returned for each `Proposal.requests` entry. The kernel carries the container without reading it; a feature reads the key it asked under. ## Indexable [Section titled “Indexable”](#indexable) > \[`key`: `string`]: `unknown` ## Properties [Section titled “Properties”](#properties) ### authentication? [Section titled “authentication?”](#authentication) > `optional` **authentication?**: [`Cacao`](/konekt/api/konekt/src/type-aliases/cacao/)\[] CACAOs returned for a CAIP-122 authentication request. # ProviderDeps > **ProviderDeps** = `object` Dependencies that tests can replace when calling [Provider.create](/konekt/api/konekt/src/classes/provider/#create). Application code normally omits this object. ## Properties [Section titled “Properties”](#properties) ### relay? [Section titled “relay?”](#relay) > `optional` **relay?**: `Relay` Relay implementation to use instead of opening a WebSocket. *** ### seed? [Section titled “seed?”](#seed) > `optional` **seed?**: `Uint8Array` Stable 32-byte seed used to authenticate to the relay. *** ### session? [Section titled “session?”](#session) > `optional` **session?**: `Pick`<`SessionClient`, `"uri"` | `"session"` | `"connect"` | `"restore"` | `"request"` | `"disconnect"`> Session implementation to use instead of constructing one. Supplying it prevents Konekt from opening a relay connection. *** ### storage? [Section titled “storage?”](#storage) > `optional` **storage?**: [`Storage`](/konekt/api/konekt/src/type-aliases/storage/) Storage dependency. This takes precedence over `CreateProviderOptions.storage`. # ProviderEvents > **ProviderEvents** = `object` ## Properties [Section titled “Properties”](#properties) ### accountsChanged [Section titled “accountsChanged”](#accountschanged) > **accountsChanged**: `string`\[] The EVM addresses currently approved by the wallet. *** ### chainChanged [Section titled “chainChanged”](#chainchanged) > **chainChanged**: [`Hex`](/konekt/api/konekt/src/type-aliases/hex/) The active EVM chain changed. The value is hexadecimal, for example `"0x1"`. *** ### connect [Section titled “connect”](#connect) > **connect**: `object` A new session was approved. `chainId` is absent when no EVM chain is configured because there is no EIP-1193 chain to report. #### chainId? [Section titled “chainId?”](#chainid) > `optional` **chainId?**: [`Hex`](/konekt/api/konekt/src/type-aliases/hex/) *** ### disconnect [Section titled “disconnect”](#disconnect) > **disconnect**: `object` The local app or remote wallet ended the session. #### code [Section titled “code”](#code) > **code**: `number` WalletConnect reason code. Local user-initiated disconnects use 6000. #### message [Section titled “message”](#message) > **message**: `string` Human-readable reason supplied locally or by the wallet. *** ### display\_uri [Section titled “display\_uri”](#display_uri) > **display\_uri**: `string` A temporary WalletConnect URI to render as a QR code or wallet link during pairing. *** ### message [Section titled “message”](#message-1) > **message**: `object` An event declared by a non-EVM forwarding adapter. #### data [Section titled “data”](#data) > **data**: `unknown` Event payload supplied by the wallet. #### type [Section titled “type”](#type) > **type**: `string` Original namespace event name. *** ### request\_sent [Section titled “request\_sent”](#request_sent) > **request\_sent**: `object` Emitted after a session request is published. `url` is present when the wallet advertised a redirect and allows deep links; application UI decides whether to open it. #### id [Section titled “id”](#id) > **id**: `number` JSON-RPC ID of the published request. #### topic [Section titled “topic”](#topic) > **topic**: `string` WalletConnect topic of the approved session. #### url [Section titled “url”](#url) > **url**: `string` | `undefined` Formatted wallet request URL, or `undefined` when no redirect is available. # RequestArguments > **RequestArguments** = `object` EIP-1193 request input. `params` must match the selected JSON-RPC method. ## Properties [Section titled “Properties”](#properties) ### method [Section titled “method”](#method) > **method**: `string` *** ### params? [Section titled “params?”](#params) > `optional` **params?**: `unknown` # RpcRequest > **RpcRequest** = `object` Request passed to chain adapters. ## Properties [Section titled “Properties”](#properties) ### chainId? [Section titled “chainId?”](#chainid) > `optional` **chainId?**: `string` CAIP-2 chain explicitly targeted for this call, or absent to use the namespace’s active chain. *** ### method [Section titled “method”](#method) > **method**: `string` JSON-RPC method name. *** ### params? [Section titled “params?”](#params) > `optional` **params?**: `unknown` Method-specific parameters supplied by the caller. # Session > **Session** = `object` An approved WalletConnect session and the namespaces granted by its wallet. ## Properties [Section titled “Properties”](#properties) ### controller [Section titled “controller”](#controller) > **controller**: `string` Public key of the wallet that controls the session. *** ### expiry [Section titled “expiry”](#expiry) > **expiry**: `number` Unix timestamp in seconds when the session expires. *** ### namespaces [Section titled “namespaces”](#namespaces) > **namespaces**: `Record`<`string`, `Namespace`> Approved methods, events, chains, and accounts grouped by namespace. *** ### pairingTopic [Section titled “pairingTopic”](#pairingtopic) > **pairingTopic**: `string` Topic of the pairing that created this session. *** ### peer [Section titled “peer”](#peer) > **peer**: `object` Connected wallet’s session identity and metadata. #### metadata [Section titled “metadata”](#metadata) > **metadata**: [`Metadata`](/konekt/api/konekt/src/type-aliases/metadata/) #### publicKey [Section titled “publicKey”](#publickey) > **publicKey**: `string` *** ### proposalRequestsResponses? [Section titled “proposalRequestsResponses?”](#proposalrequestsresponses) > `optional` **proposalRequestsResponses?**: [`ProposalRequestsResponses`](/konekt/api/konekt/src/type-aliases/proposalrequestsresponses/) Responses to feature-owned requests that were attached to the session proposal. *** ### relay [Section titled “relay”](#relay) > **relay**: `object` Relay protocol selected by the wallet. #### protocol [Section titled “protocol”](#protocol) > **protocol**: `string` *** ### self [Section titled “self”](#self) > **self**: `object` This application’s session identity and metadata. #### metadata [Section titled “metadata”](#metadata-1) > **metadata**: [`Metadata`](/konekt/api/konekt/src/type-aliases/metadata/) #### publicKey [Section titled “publicKey”](#publickey-1) > **publicKey**: `string` *** ### sessionConfig? [Section titled “sessionConfig?”](#sessionconfig) > `optional` **sessionConfig?**: `object` Wallet-provided session behavior, including whether request deep links are disabled. #### disableDeepLink? [Section titled “disableDeepLink?”](#disabledeeplink) > `optional` **disableDeepLink?**: `boolean` *** ### topic [Section titled “topic”](#topic) > **topic**: `string` Topic used to encrypt and route session requests. # Storage > **Storage** = `object` Asynchronous key-value storage used for the relay identity and session. The shape is compatible with wrappers around browser storage, mobile storage, and test stores. ## Methods [Section titled “Methods”](#methods) ### getItem() [Section titled “getItem()”](#getitem) > **getItem**(`key`): `Promise`<`string` | `null`> Reads a value, returning `null` when the key does not exist. #### Parameters [Section titled “Parameters”](#parameters) ##### key [Section titled “key”](#key) `string` #### Returns [Section titled “Returns”](#returns) `Promise`<`string` | `null`> *** ### removeItem() [Section titled “removeItem()”](#removeitem) > **removeItem**(`key`): `Promise`<`void`> Removes a value. #### Parameters [Section titled “Parameters”](#parameters-1) ##### key [Section titled “key”](#key-1) `string` #### Returns [Section titled “Returns”](#returns-1) `Promise`<`void`> *** ### setItem() [Section titled “setItem()”](#setitem) > **setItem**(`key`, `value`): `Promise`<`void`> Creates or replaces a value. #### Parameters [Section titled “Parameters”](#parameters-2) ##### key [Section titled “key”](#key-2) `string` ##### value [Section titled “value”](#value) `string` #### Returns [Section titled “Returns”](#returns-2) `Promise`<`void`> # TtlConfig > **TtlConfig** = `object` WalletConnect protocol lifetimes, in seconds. ## Properties [Section titled “Properties”](#properties) ### minPublish [Section titled “minPublish”](#minpublish) > **minPublish**: `number` Minimum relay storage window, in seconds, independent of the payload expiry. *** ### propose [Section titled “propose”](#propose) > **propose**: `number` How long a pairing proposal stays valid, in seconds. *** ### request [Section titled “request”](#request) > **request**: `number` How long a wallet has to answer a request before it is rejected locally, in seconds. *** ### session [Section titled “session”](#session) > **session**: `number` Lifetime of a settled session, in seconds. # RpcErrorCode > `const` **RpcErrorCode**: `object` JSON-RPC and EIP-1193 error codes thrown directly by Konekt. ## Type Declaration [Section titled “Type Declaration”](#type-declaration) ### invalidParams [Section titled “invalidParams”](#invalidparams) > `readonly` **invalidParams**: `-32602` = `-32602` JSON-RPC invalid parameters. ### unauthorized [Section titled “unauthorized”](#unauthorized) > `readonly` **unauthorized**: `4100` = `4100` EIP-1193 unauthorized: a wallet method was requested without a session. ### unsupportedMethod [Section titled “unsupportedMethod”](#unsupportedmethod) > `readonly` **unsupportedMethod**: `4200` = `4200` EIP-1193 unsupported method or missing EVM read transport. # TTL > `const` **TTL**: [`TtlConfig`](/konekt/api/konekt/src/type-aliases/ttlconfig/) Default WalletConnect protocol lifetimes, in seconds. # konekt-monorepo ## Modules [Section titled “Modules”](#modules) * [konekt-ui/src](/konekt/api/konekt-ui/src/readme/) * [konekt-ui/src/wagmi](/konekt/api/konekt-ui/src/wagmi/readme/) * [konekt/src](/konekt/api/konekt/src/readme/) * [konekt/src/chains/bip122](/konekt/api/konekt/src/chains/bip122/readme/) * [konekt/src/chains/cosmos](/konekt/api/konekt/src/chains/cosmos/readme/) * [konekt/src/chains/eip155](/konekt/api/konekt/src/chains/eip155/readme/) * [konekt/src/chains/generic](/konekt/api/konekt/src/chains/generic/readme/) * [konekt/src/chains/solana](/konekt/api/konekt/src/chains/solana/readme/) * [konekt/src/features/cacao](/konekt/api/konekt/src/features/cacao/readme/) * [konekt/src/features/siwe](/konekt/api/konekt/src/features/siwe/readme/) * [konekt/src/http](/konekt/api/konekt/src/http/readme/) # Authentication features > Request Sign-In with Ethereum during pairing and verify the returned CACAO safely on your server. Konekt can ask a wallet to authenticate while it approves the WalletConnect session. This is often called **one-click authentication** because connection and sign-in happen in one wallet flow. The built-in `siwe()` feature follows Sign-In with Ethereum and CAIP-122. The wallet returns a signed **CACAO** (Chain Agnostic CApability Object) containing the account, domain, URI, nonce, and time limits. Authentication has two separate jobs: 1. The browser asks the wallet to sign and binds the answer to the connected account. 2. The server verifies the signature and the claims before creating an authenticated app session. The browser is not a trust boundary. Do not treat the presence of a CACAO as proof by itself. ## Request authentication in the browser [Section titled “Request authentication in the browser”](#request-authentication-in-the-browser) Add `siwe()` to the provider’s `features`: ```ts import { Provider } from "konekt"; import { ethereumMainnet } from "konekt/eip155"; import { siwe, cacaosOf } from "konekt/siwe"; async function getNonce() { const response = await fetch("/auth/nonce", { credentials: "include" }); if (!response.ok) throw new Error("Could not create a sign-in challenge"); return response.text(); } const provider = await Provider.init({ projectId, metadata, chains: [ethereumMainnet], features: [ siwe({ domain: location.host, uri: location.origin, chains: ["eip155:1"], getNonce, }), ], }); const session = await provider.connect(); const cacaos = cacaosOf(session); const response = await fetch("/auth/verify", { method: "POST", headers: { "content-type": "application/json" }, credentials: "include", body: JSON.stringify({ cacaos }), }); if (!response.ok) throw new Error("Sign-in failed"); ``` `getNonce` runs immediately before each proposal is published, so it can fetch a fresh challenge from your server. The server should generate a cryptographically random nonce, associate it with the current browser session, and allow it to be used only once. During settlement, Konekt checks that every returned CACAO: * has the nonce, domain, and URI this provider requested; * belongs to an account approved in the WalletConnect session. If one of those checks fails, `connect()` rejects and Konekt tears down the new session. Signature verification still belongs on the server. ### SIWE options [Section titled “SIWE options”](#siwe-options) | Option | Purpose | | ----------- | ------------------------------------------------------------------------------------------------------------ | | `domain` | The host shown to the wallet, usually `location.host`. | | `uri` | The exact application URI, usually `location.origin`. | | `chains` | CAIP-2 IDs the user may authenticate with, such as `["eip155:1"]`. | | `getNonce` | Returns a fresh, server-issued nonce for each connection attempt. | | `statement` | Optional human-readable reason for signing in. It cannot contain line breaks. | | `exp` | Optional ISO timestamp after which the message is invalid. | | `nbf` | Optional ISO timestamp before which the message is invalid. | | `requestId` | Optional application-specific request identifier. | | `resources` | Optional resource URIs covered by the sign-in message. | | `required` | Whether connection must fail when the wallet does not answer the authentication request. Defaults to `true`. | Not every wallet supports proposal authentication. If unauthenticated connections are useful in your app, set `required: false` and check the result explicitly: ```ts const session = await provider.connect(); const cacaos = cacaosOf(session); if (cacaos.length === 0) { // Connected, but not signed in. } ``` Recap resources (`urn:recap:`) are not implemented. Passing a `urn:recap:` entry in `resources` makes `siwe()` throw immediately, and `verifyCacao()` reports `unverifiable` for a message that carries one, so neither side can silently ignore a capability it does not enforce. ## Verify authentication on the server [Section titled “Verify authentication on the server”](#verify-authentication-on-the-server) The server must validate both the signed message and the claims inside it: * `verifyCacao()` checks the cryptographic signature. * `checkClaims()` checks the domain and nonce, enforces the `exp` and `nbf` time limits, and compares the audience URI when you pass `uri`. Neither check replaces the other. A valid signature over an old or attacker-issued nonce is not a valid login. Both functions return `valid`, `invalid`, or `unverifiable`: | Status | Meaning | Authentication decision | | -------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------- | | `valid` | The check passed. | Continue only after both checks are valid. | | `invalid` | The signature or a claim is wrong. | Reject authentication. | | `unverifiable` | This process could not complete the check, for example because smart-account RPC is unavailable. | Do not authenticate; retry or report a temporary failure. | `unverifiable` does not prove forgery, but it is never safe to treat it as success. A wallet returns one CACAO per authenticated account, so the browser posts an array. Verify each one, then consume the nonce once for the whole request: ```ts import type { Cacao } from "konekt"; import { checkClaims, verifyCacao } from "konekt/cacao"; import { http } from "konekt/http"; declare function loadIssuedNonce(browserSessionId: string): Promise; declare function consumeIssuedNonce(browserSessionId: string, nonce: string): Promise; const call = http("https://ethereum.example-rpc.com"); async function verifyOne(cacao: Cacao, nonce: string): Promise { const claims = checkClaims(cacao.p, { domain: "app.example.com", uri: "https://app.example.com", nonce, }); if (claims.status !== "valid") throw new Error(claims.reason); const signature = await verifyCacao(cacao, { call }); if (signature.status !== "valid") throw new Error(signature.reason); return cacao.p.iss; } async function authenticate(cacaos: Cacao[], browserSessionId: string) { if (cacaos.length === 0) throw new Error("The wallet did not authenticate"); const nonce = await loadIssuedNonce(browserSessionId); const issuers: string[] = []; for (const cacao of cacaos) { issuers.push(await verifyOne(cacao, nonce)); } // Make this an atomic compare-and-delete. Only one request may succeed. if (!(await consumeIssuedNonce(browserSessionId, nonce))) { throw new Error("This sign-in challenge was already used"); } return issuers; } ``` Each issuer is a `did:pkh` string such as `did:pkh:eip155:1:0x…`. Use `parseDidPkh()` from `konekt/cacao` to read its namespace, reference, and address. The wallet lists the account it considers primary first; sign the user in as that account and treat the rest as additional proven addresses. The `call` option is needed for EIP-1271 smart contract accounts. It must reach JSON-RPC for the issuer’s chain. Ordinary EIP-191 account signatures can be checked without it. Consume the nonce once per request, as above. Consuming it inside the loop makes every CACAO after the first fail. ## Write a custom feature [Section titled “Write a custom feature”](#write-a-custom-feature) A feature is a plain object passed in `features`. It owns one key under `Proposal.requests` and reads the wallet’s answer back from the matching key of `Session.proposalRequestsResponses`. Konekt carries both containers without interpreting them, so a new feature is not a change to the provider. ```ts import type { Feature } from "konekt"; export function greeting(text: string): Feature { let sent: string | undefined; return { name: "greeting", async onProposal(proposal) { sent = text; return { ...proposal, requests: { ...proposal.requests, greeting: { text } } }; }, onSettle(session) { const answer = session.proposalRequestsResponses?.greeting; if (sent && !answer) throw new Error("The wallet ignored the greeting request"); }, onDisconnect() { sent = undefined; }, }; } ``` The contract in full: * `name` is required. Konekt uses it in diagnostics. * `onProposal` is awaited before the proposal is published, so it may fetch a server challenge. Return the proposal you want published; returning nothing keeps the current one. * `onSettle` runs after the wallet approves. Throwing rejects `connect()` and disconnects the session Konekt just settled, so it never leaves a half-authenticated session behind. * `onDisconnect` clears feature-owned state. Features participate in connection setup. They do not wrap or intercept `provider.request()`. # konekt-ui > Add an accessible React wallet picker, pairing QR, and optional wagmi account controls. `konekt-ui` is an optional React interface for Konekt. It can list compatible wallets, show the pairing QR, open wallet links, and report connection errors. Try it live The [konekt showcase](https://lsheva.github.io/konekt/showcase/) pairs a raw `Provider` through `WalletModal` and `useProviderPairing`, then exercises every method the session settles. About 97% smaller than AppKit in a real app A Vite React app with Konekt UI first-loads **19.06 kB** and totals **45.52 kB**. The same shell with `@reown/appkit@1.8.23` first-loads **721.26 kB** and totals **1079.28 kB**—**97.4%** smaller on first load and **95.8%** smaller overall. React is marked external in both builds. The modal itself is **13.28 kB**. Choose an entry point: | Import | Use it when | | --------------------------- | ------------------------------------------------------------------------------------------------------------ | | `konekt-ui` | You have a Konekt `Provider`. Works with every configured namespace and does not require wagmi. | | `konekt-ui/wagmi` | Your EVM app already manages connectors and account state with wagmi. | | `konekt-ui/wallet-standard` | Your Solana app should list injected extensions (Phantom, Solflare, Backpack) next to WalletConnect pairing. | | `konekt-ui/cosmos` | Your Cosmos app should list Keplr-API extensions (Keplr, Leap) next to WalletConnect pairing. | The components require React 18 or newer. The wagmi entry point also requires wagmi 2 or 3 and viem 2; the wallet-standard and cosmos entry points need only React. ## Konekt UI vs Reown AppKit [Section titled “Konekt UI vs Reown AppKit”](#konekt-ui-vs-reown-appkit) Konekt UI is better when the app needs a wallet picker, pairing QR, and account controls without adopting a full onboarding platform. | UI path | First load | Overall | | ------------------------------------ | ------------- | -------------- | | Vite app with Konekt `WalletModal` | **19.06 kB** | **45.52 kB** | | Vite app with `@reown/appkit@1.8.23` | **721.26 kB** | **1079.28 kB** | Those rows are production builds of `packages/size-konekt-ui` and `packages/size-appkit`, with React marked external. The Konekt modal and stylesheet alone are **13.28 kB** (10.30 kB JavaScript and 2.98 kB CSS); the wagmi `ConnectButton` path is **14.87 kB** with the same stylesheet. AppKit remains a broader product, but even with email, socials, swaps, on-ramp, and analytics disabled it still first-loads wallet-list and email UI. | Capability | Konekt UI | Reown AppKit | | -------------------------------------------------------- | --------- | ------------ | | Wallet picker and pairing QR | Yes | Yes | | Mobile wallet links | Yes | Yes | | Light, dark, and system themes | Yes | Yes | | App-owned styling and unstyled mode | Yes | Theming APIs | | Optional wagmi account, network, and disconnect controls | Yes | Yes | | Embedded email and social wallets | No | Yes | | Smart accounts | No | Yes | | Built-in swaps and on-ramp | No | Yes | That narrower scope is the advantage for apps that already own authentication, transactions, RPC access, and visual design. Konekt UI does not make those applications download or configure unrelated product features. AppKit is the better fit only when the app wants its broader onboarding and transaction suite. ## Install [Section titled “Install”](#install) ```sh pnpm add konekt konekt-ui react ``` Import the default stylesheet once near your app’s entry point: ```ts import "konekt-ui/styles.css"; ``` Skip the stylesheet only when you plan to use the `unstyled` option and supply all component styles yourself. ## Use WalletModal with a provider [Section titled “Use WalletModal with a provider”](#use-walletmodal-with-a-provider) `useProviderPairing()` adapts a Konekt provider to the state and actions required by `WalletModal`: ```tsx import { useState } from "react"; import type { Provider } from "konekt"; import { useProviderPairing, WalletModal } from "konekt-ui"; import "konekt-ui/styles.css"; export function WalletConnection({ provider }: { provider: Provider }) { const [open, setOpen] = useState(false); const pairing = useProviderPairing(provider); return ( <> setOpen(false)} /> ); } ``` The pairing carries the provider’s WalletConnect project ID, and the modal sends it to the WalletConnect Explorer when loading wallet listings — there is no separate ID to pass. ### What the modal does on its own [Section titled “What the modal does on its own”](#what-the-modal-does-on-its-own) On a desktop browser, pairing does not begin when the modal opens. It begins when the user picks a wallet or the WalletConnect option and reaches the QR view. From there the modal: 1. calls `provider.connect({ signal })`; 2. renders the URI from `display_uri`; 3. replaces a pairing that is about to lapse with a fresh one, calling `onDismiss` for the discarded attempt; 4. aborts the pending connection and calls `onDismiss` if the user leaves before it finishes; 5. closes itself once the provider connects, by calling `onClose`. Because it closes itself, keep `open` as controlled state and let `onClose` set it to `false`. The modal also skips pairing entirely when `pairing.connected` is already true. ### On a phone [Section titled “On a phone”](#on-a-phone) A phone gets a different flow, and the difference is not cosmetic. WebKit refuses to leave for a wallet’s custom scheme once the tap that asked for it has expired, and a pairing URI takes a relay round trip to arrive — so a modal that pairs on tap can never deep link on iOS. The modal therefore: * pairs as soon as it opens, so a URI is in hand before the user chooses; * leaves for the wallet inside the tap itself, and shows “Continue in Wallet” with an **Open** button rather than a QR code nobody can scan with the phone they are holding; * lists only wallets that advertised a mobile link, because a desktop-only listing cannot be reached from a phone; * opens automatically once per chosen wallet. A replaced pairing waits to be asked, so the page never navigates away on its own. Pairing early means a socket opens for a modal the user may only browse. That is the price of the redirect working at all. On its own, the provider adapter lists WalletConnect Explorer wallets and the generic QR option. Pass `sources` to also list injected browser wallets (see [Injected wallets](#injected-wallets-without-wagmi)), or use the wagmi adapter when wagmi already manages your EVM connectors. ### `WalletModal` props [Section titled “WalletModal props”](#walletmodal-props) | Prop | Type | Purpose | | ----------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `open` | `boolean` | Whether the dialog renders. Required. | | `pairing` | `Pairing` | From `useProviderPairing()` or `useWagmiPairing()`. Required. Carries the project ID for Explorer queries. | | `onClose` | `() => void` | Asks the parent to set `open` to `false`. Required. | | `chains` | `readonly string[]` | CAIP-2 IDs used to filter Explorer results. Defaults to the provider’s chains. | | `wallets` | `WalletFilter` | `include`, `exclude`, and `featured` Explorer IDs. | | `onDismiss` | `() => void` | Runs when an unfinished pairing is discarded: the user left, or it was replaced before lapsing. | | `theme` | `"light" \| "dark" \| "system"` | Color scheme. Defaults to `"system"`. | | `className` | `string` | Extra class on the root. | | `style` | `WcStyle` | Inline styles plus `--kui-*` token overrides. | | `unstyled` | `boolean` | Drops the default `kui-*` classes, keeping `data-kui` attributes. | `className`, `style`, `theme`, and `unstyled` are shared by every konekt-ui component. ## Injected wallets without wagmi [Section titled “Injected wallets without wagmi”](#injected-wallets-without-wagmi) wagmi remains the path for EVM apps: it already discovers injected EVM wallets and owns their account state, so konekt-ui only mirrors its connectors. Solana and Cosmos have no wagmi. For them, `useProviderPairing` accepts `sources` — discovery hooks whose wallets appear as installed choices next to WalletConnect pairing: | Import | Ecosystem | Discovery | | --------------------------- | --------- | ------------------------------------------------------------- | | `konekt-ui/wallet-standard` | Solana | Wallet Standard announce events (Phantom, Solflare, Backpack) | | `konekt-ui/cosmos` | Cosmos | Probes `window.keplr`-shaped extensions (Keplr, Leap) | `konekt-ui/wallet-standard` is for Solana. The underlying announce protocol is chain-agnostic, but this entry point lists only wallets that serve `solana:` chains unless you pass an explicit `chains` filter. Sources are discovery only. Connecting an injected wallet never touches the Konekt provider, and after `onConnect` the app owns the wallet handle: accounts, signing, and disconnects come from that handle, not from the modal. ```tsx import { useState } from "react"; import type { Provider } from "konekt"; import { useProviderPairing, WalletModal } from "konekt-ui"; import { type CosmosInjectedWallet, useCosmosSource } from "konekt-ui/cosmos"; import { useWalletStandardSource, type WalletStandardWallet } from "konekt-ui/wallet-standard"; export function MultiChainConnection({ provider }: { provider: Provider }) { const [open, setOpen] = useState(false); const [solanaWallet, setSolanaWallet] = useState(); const [cosmosWallet, setCosmosWallet] = useState(); const solana = useWalletStandardSource({ onConnect: setSolanaWallet }); const cosmos = useCosmosSource({ chainIds: ["cosmoshub-4"], onConnect: setCosmosWallet }); const pairing = useProviderPairing(provider, { sources: [solana, cosmos] }); return ( <> setOpen(false)} /> {solanaWallet &&

Solana: {solanaWallet.accounts[0]?.address}

} {cosmosWallet &&

Cosmos wallet enabled.

} ); } ``` After `onConnect`, sign with the handle’s own API: the Wallet Standard wallet exposes features such as `solana:signMessage` and `solana:signTransaction`, and the Keplr handle offers offline signers for CosmJS directly. Injected wallets do not need the signing bridges from the [Solana](../solana/) and [CosmJS](../cosmjs/) guides — those exist only for signing over a Konekt session. ### `useWalletStandardSource` options [Section titled “useWalletStandardSource options”](#usewalletstandardsource-options) | Option | Type | Purpose | | ----------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `onConnect` | `(wallet: WalletStandardWallet) => void` | Receives the connected wallet. Required. | | `chains` | `readonly string[]` | Wallet Standard chain ids a wallet must serve, e.g. `"solana:mainnet"`. Defaults to any `solana:` chain. These are Wallet Standard network names, not the genesis-hash CAIP-2 ids Konekt chains use. | | `onError` | `(error: Error) => void` | Receives connect failures, e.g. a dismissed extension prompt. | ### `useCosmosSource` options [Section titled “useCosmosSource options”](#usecosmossource-options) | Option | Type | Purpose | | ----------- | ---------------------------------------- | ---------------------------------------------------------------------- | | `chainIds` | `readonly string[]` | Cosmos chain ids passed to `enable`, e.g. `["cosmoshub-4"]`. Required. | | `onConnect` | `(wallet: CosmosInjectedWallet) => void` | Receives the enabled wallet. Required. | | `onError` | `(error: Error) => void` | Receives enable failures. | ### Writing your own source [Section titled “Writing your own source”](#writing-your-own-source) A source is a plain object, so an app can supply discovery konekt-ui does not ship — for example EIP-6963 announcements in a vanilla EVM app that does not use wagmi: ```ts import type { LocalWalletSource } from "konekt-ui"; declare const eip6963Wallets: LocalWalletSource["wallets"]; const injectedEvm: LocalWalletSource = { wallets: eip6963Wallets, connect: (wallet) => { // request accounts on the announced provider and keep the handle }, connected: false, }; ``` Each source owns its wallets: the modal routes a clicked wallet back to the source whose `wallets` contains it, and a source turning `connected` closes the modal. ## wagmi [Section titled “wagmi”](#wagmi) Install the optional peers (React 18+, wagmi 2 or 3, viem 2): ```sh pnpm add konekt konekt-ui react viem wagmi ``` `ConnectButton` uses the connectors already registered in your wagmi config: * a connector whose `id` or `type` is `"konekt"` provides WalletConnect pairing; * other connectors appear as installed wallet choices, injected ones only while their provider is in the browser: a config registers `injected()` whether or not an extension answers, and mobile Safari usually has none; * a named EIP-6963 entry hides the generic injected connector, so one wallet is one row; * after connection, the button opens account, network, and disconnect controls. ```tsx import { ConnectButton } from "konekt-ui/wagmi"; import "konekt-ui/styles.css"; export function WalletControls() { return ; } ``` The wagmi entry point also exports the connector: register `konekt(options)` from `konekt-ui/wagmi` in `createConfig()`. It delays `Provider.init()` until first use, so static registration does not open a relay socket. The complete setup is in the [wagmi integration guide](../wagmi/). ### `ConnectButton` props [Section titled “ConnectButton props”](#connectbutton-props) | Prop | Type | Purpose | | ------------------ | -------------------------- | ------------------------------------------------------------------------------------------------ | | `chains` | `readonly string[]` | CAIP-2 IDs for wallet filtering. Defaults to the configured wagmi chains. | | `wallets` | `WalletFilter` | `include`, `exclude`, and `featured` Explorer IDs. | | `getWalletConnect` | `() => Promise` | Supplies the Konekt connector when the wagmi config does not already contain one. | | `projectId` | `string` | Explorer queries, only with `getWalletConnect` — a registered Konekt connector supplies its own. | | `onDismiss` | `() => void` | Cancels connector-owned pairing work when the user closes the modal. | It also accepts the shared `theme`, `className`, `style`, and `unstyled` props. Three of these cover the less common cases: * `getWalletConnect` is a `ConnectButton` prop (and a `useWagmiPairing()` option) that returns the WalletConnect connector on demand, for apps that keep it out of `createConfig()` so a visitor who never connects never loads Konekt. Pass `projectId` alongside it, because there is no registered connector to read the ID from until pairing starts. See the [wagmi guide](../wagmi/#static-and-lazy-connector-registration) for the trade-off it carries. * `onDismiss` runs when the user closes the modal, so connector-owned work can be cancelled alongside the pairing. * `useWagmiPairing()` gives you the same pairing state without `ConnectButton`, for a custom trigger rendered with `WalletModal`. ## Which wallets, which networks [Section titled “Which wallets, which networks”](#which-wallets-which-networks) By default, `WalletModal` asks the Explorer for wallets that support one of the provider’s configured chains. Override that list with CAIP-2 IDs: ```tsx import { useState } from "react"; import type { Provider } from "konekt"; import { useProviderPairing, WalletModal } from "konekt-ui"; // Copy the IDs from https://walletconnect.com/explorer const featuredWalletIds = ["…", "…"]; const hiddenWalletIds = ["…"]; export function WalletPicker({ provider }: { provider: Provider }) { const [open, setOpen] = useState(false); const pairing = useProviderPairing(provider); return ( setOpen(false)} chains={["eip155:1", "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"]} wallets={{ featured: featuredWalletIds, exclude: hiddenWalletIds }} /> ); } ``` Wallet filter values are WalletConnect Explorer IDs, not connector IDs or reverse-domain names. | Filter | Effect | | ---------- | -------------------------------------------------------------- | | `include` | Show only these Explorer wallets. | | `featured` | Put these wallets on the modal’s first screen. | | `exclude` | Remove these wallets from Explorer results as each page loads. | Filters do not hide installed wagmi connectors. Control those in your wagmi configuration. ## Theme and custom styles [Section titled “Theme and custom styles”](#theme-and-custom-styles) The default `theme="system"` follows the user’s operating-system color scheme. Pass `theme="light"` or `theme="dark"` to lock it. Override design tokens through `style`: ```tsx setOpen(false)} theme="dark" style={{ "--kui-accent": "#7c5cff", "--kui-radius": "20px", }} /> ``` Pass `unstyled` to remove default `kui-*` classes. Stable `data-kui` and `data-kui-slot` attributes remain for your selectors. When supplying custom styles, preserve visible keyboard focus, sufficient color contrast, the QR code’s square dimensions, and a clear error state. ## Built-in dialog behavior [Section titled “Built-in dialog behavior”](#built-in-dialog-behavior) The shared modal component: * moves focus into the dialog when it opens; * keeps Tab focus inside the dialog; * closes on Escape or backdrop activation; * restores focus to the previously focused element; * exposes the dialog title and control labels to assistive technology. If you compose the lower-level `Modal` or `QrCode` exports yourself, provide concise visible instructions alongside them. A QR code alone is not enough for someone who cannot scan it; offer a wallet link or copy action when possible. ## Building your own picker [Section titled “Building your own picker”](#building-your-own-picker) `WalletModal` is one arrangement of smaller exports. Use them directly when you need a different one. | Export | From | Purpose | | --------------------- | ----------------- | -------------------------------------------------------------------------- | | `Modal` | `konekt-ui` | The accessible dialog shell: focus trap, Escape, backdrop, restored focus. | | `QrCode` | `konekt-ui` | Renders a pairing URI as a QR code. | | `Avatar` | `konekt-ui` | Address-derived gradient disc used by the account chip. | | `truncateAddress` | `konekt-ui` | Shortens a hex address for a chip or heading. | | `fetchWallets` | `konekt-ui` | Queries the WalletConnect Explorer. Returns one page of listings. | | `filterWallets` | `konekt-ui` | Applies `include`, `exclude`, and `featured` to listings. | | `FEATURED_WALLET_IDS` | `konekt-ui` | Default featured Explorer IDs. | | `walletLink` | `konekt-ui` | The base URL a listing advertised for one platform, or nothing. | | `walletHref` | `konekt-ui` | Builds a wallet deep link from a listing and a pairing URI. | | `openWalletLink` | `konekt-ui` | Navigates to a wallet link. Call it inside the tap that asked for it. | | `isMobile` | `konekt-ui` | Whether to prefer deep links over a QR code. | | `pairingExpiry` | `konekt-ui` | The deadline a pairing URI carries, in unix seconds. | | `pairingRefreshDelay` | `konekt-ui` | How long that URI may still be offered, in milliseconds. | | `AccountModal` | `konekt-ui/wagmi` | The connected account and network dialog `ConnectButton` opens. | `AccountModal` is controlled through `open`, `view` (`"account"` or `"networks"`), `onView`, and `onClose`, so a custom button can reuse the account and network switching UI without `ConnectButton`. # Wallet UI > Build your own pairing QR, cancel connection attempts, and open a wallet for session requests. Konekt reports wallet UI work through events. It does not render a QR code, navigate to a wallet, or open a browser tab on its own. Use: * `display_uri` while creating a new session; * `request_sent` when an approved session receives a signing or transaction request. If you prefer a ready-made React interface, use [konekt-ui](../konekt-ui/). ## Show a pairing QR [Section titled “Show a pairing QR”](#show-a-pairing-qr) Listen for `display_uri` before you call `connect()`. The event payload is a `wc:` URI that a WalletConnect-compatible wallet can scan: ```ts const onUri = (uri: string) => { renderQrCode(uri); }; provider.on("display_uri", onUri); try { await provider.connect(); } finally { provider.off("display_uri", onUri); hideQrCode(); } ``` `display_uri` is emitted only when Konekt needs a new pairing. A restored session is already connected and does not need another QR. Treat the URI as temporary secret material: show it only for the current attempt, do not include it in analytics, and remove it when the attempt finishes. ### Let the user cancel [Section titled “Let the user cancel”](#let-the-user-cancel) Pass an `AbortSignal` to stop a pending proposal when the user closes your UI: ```ts const controller = new AbortController(); function closePairingDialog() { controller.abort(); } await provider.connect({ signal: controller.signal }); ``` `connect()` rejects with an `AbortError` `DOMException`, which is a cancellation rather than a failure. Treat it separately from a wallet rejection: ```ts async function connectWallet() { const controller = new AbortController(); try { await provider.connect({ signal: controller.signal }); } catch (error) { if (error instanceof DOMException && error.name === "AbortError") return; showConnectionError(error); } } ``` Create a new controller for each connection attempt, and create it at the moment the user starts connecting. A controller that was already aborted before you pass it does not cancel anything, and the attempt stays pending until the proposal expires. ## Open the wallet for a request [Section titled “Open the wallet for a request”](#open-the-wallet-for-a-request) After pairing, the user may still need to return to their wallet to approve a signature or transaction. `request_sent` fires after Konekt publishes that request. ```ts provider.on("request_sent", ({ id, topic, url }) => { if (url) { window.location.assign(url); } }); ``` The event contains: | Field | Meaning | | ------- | --------------------------------------------------------------------------------------------------------- | | `id` | The JSON-RPC request ID. | | `topic` | The WalletConnect session topic. | | `url` | A wallet URL when the wallet advertised a redirect and did not disable deep links; otherwise `undefined`. | Your app decides whether and when to navigate. This avoids unexpected navigation and lets you adapt the behavior for desktop browsers, mobile browsers, and embedded apps. Register this listener once during app setup, before sending requests. ### Build a request URL yourself [Section titled “Build a request URL yourself”](#build-a-request-url-yourself) If your app already knows the wallet’s native or universal URL, `formatWalletRedirect()` adds the current request ID and session topic: ```ts import { formatWalletRedirect } from "konekt"; const walletHref = "https://metamask.app.link"; provider.on("request_sent", ({ id, topic }) => { window.location.assign(formatWalletRedirect(walletHref, id, topic)); }); ``` Telegram Mini App URLs (`https://t.me/...`) receive a `startapp` payload. Other URLs receive a `/wc?requestId=…&sessionTopic=…` path. This helper formats a request redirect for an existing session. It does not put the initial pairing URI into a wallet deep link. ## Other provider events [Section titled “Other provider events”](#other-provider-events) The provider also exposes standard connection and account events: | Event | Payload | When to use it | | ----------------- | ----------------------------------------------------------------------- | -------------------------------------------------------- | | `connect` | `{ chainId?: "0x1" }` — hex, and absent when no EVM chain is configured | Mark a newly approved session as connected. | | `disconnect` | `{ code, message }` | Clear connected UI and app state. | | `accountsChanged` | `string[]` | Refresh the selected EVM account. | | `chainChanged` | Hex chain ID such as `"0x1"` | Refresh chain-specific EVM state. | | `message` | `{ type, data }` | Handle declared events from non-EVM forwarding adapters. | `disconnect` fires both when your app calls `provider.disconnect()` and when the wallet ends the session. Konekt emits it once per ended session, so use it as the single place that clears connected state. Parse `chainChanged` defensively Konekt emits its own `chainChanged` as hex, but when a wallet sends the event Konekt forwards the wallet’s original string. A wallet that sends `"1"` instead of `"0x1"` reaches your listener unchanged. Use `Number(chainId)`, which reads both forms, rather than assuming a `0x` prefix. Keep the exact listener function so you can remove it with `off()`: ```ts const onDisconnect = () => { showDisconnectedState(); }; provider.on("disconnect", onDisconnect); provider.off("disconnect", onDisconnect); ```