One extension,
four chains
Qiubit exposes EVM, Octra, Solana and Sui from a single wallet. It speaks EIP-1193, EIP-6963 and the Solana and Sui Wallet Standards, so most stacks find it without any code from you. This page covers the rest.
pnpm add @qiubit/wallet-sdk @qiubit/reactimport { ConnectQiubit } from "@qiubit/react"; export function Nav() { return <ConnectQiubit chain="evm" appName="My dApp" />;}- 4chains
- EVM · Octra · Solana · Sui
- 3standards
- Discovered without your code
- 1component
- <ConnectQiubit />
- 0adapters
- For three of the four
What you have to write, per chain
Only one of the four asks anything of you. Read the last column first.
- EVMNone
Speaks
EIP-1193 · EIP-6963
Reach it with
wagmi · viem · RainbowKit
- OctraAdapter
Speaks
No wallet standard
Reach it with
connectOctra()
- SolanaNone
Speaks
Wallet Standard
Reach it with
@solana/wallet-adapter
- SuiNone
Speaks
Wallet Standard
Reach it with
Sui dApp Kit
Connecting takes one component
Install, drop in the button, done. The states people actually hit are handled for you.
Install
Two packages. The SDK is framework-free; the React package is the button and the hook.
pnpm add @qiubit/wallet-sdk# React apps, optionalpnpm add @qiubit/reactConnect
One component covers every state a connection can be in — including the two most integrations skip: the extension still injecting, and the person rejecting the approval.
import { ConnectQiubit } from "@qiubit/react"; export function Nav() { return <ConnectQiubit chain="evm" appName="My dApp" />;}Without React
The SDK has no framework dependency. connectEvm resolves with the address and chain, or throws a QiubitError you can branch on.
import { connectEvm, QiubitError } from "@qiubit/wallet-sdk"; try { const { address, chainId } = await connectEvm(); console.log(address, chainId);} catch (err) { if (err instanceof QiubitError && err.isUserRejection) { // A choice, not a fault. Show nothing. }}What each chain needs
Three of the four need nothing beyond the standards. Octra is the exception, and here is why.
EVM
Standard EIP-1193 on window.ethereum, announced over EIP-6963 as io.qiubit.wallet. wagmi, viem and RainbowKit discover it with no adapter.
import { connectEvm, signMessage, switchChain } from "@qiubit/wallet-sdk"; const { address } = await connectEvm();const signature = await signMessage("gm", address); // Adds the chain first when the wallet answers 4902.await switchChain(8453, { chainId: 8453, chainName: "Base", rpcUrls: ["https://mainnet.base.org"], nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },});Octra
Octra has its own connect rather than eth_requestAccounts: the approval is scoped to the Octra network, and it returns both addresses the wallet derives from one seed.
It is also the only chain here with no wallet standard behind it, so @qiubit/adapters ships an adapter for it.
import { connectOctra } from "@qiubit/wallet-sdk"; const { octraAddress, evmAddress } = await connectOctra("My dApp");Solana and Sui
Both register as Wallet Standard wallets, so @solana/wallet-adapter and Sui dApp Kit list Qiubit without any code from you. Use the SDK only when you want to skip those libraries.
import { connectSolana, solanaSignMessage } from "@qiubit/wallet-sdk"; const connection = await connectSolana();const { signature } = await solanaSignMessage( connection, new TextEncoder().encode("gm"),);Every failure has a name
A rejected approval and a missing wallet need opposite handling. The SDK never collapses them into one null.
| Code | What happened | What to do |
|---|---|---|
| NOT_INSTALLED | No Qiubit on the page | Link to qiubitwallet.com/downloads |
| USER_REJECTED | Rejected in the wallet | Nothing. It is a choice, not a fault |
| UNAUTHORIZED | Site not authorised, or wallet locked | Prompt to connect again |
| CHAIN_NOT_ADDED | Wallet does not know that chain | Pass addIfMissing to switchChain |
| UNSUPPORTED_METHOD | Wallet does not expose that call | Check the chain's feature list |
| REQUEST_FAILED | Anything else | Show the message; it is a real failure |
The three things that go wrong
All three are environment problems rather than bugs, and all three have a specific fix.
The wallet is installed but not detected
Almost always a race: the page read window.ethereum before the extension injected. detectQiubit listens for the EIP-6963 announcement and polls the window until a timeout, so it survives either order.
If you check the window yourself, you will hit this. Use the SDK, or wait for the announcement.
import { detectQiubit } from "@qiubit/wallet-sdk"; // Resolves null when it is genuinely absent — never throws.const provider = await detectQiubit({ timeoutMs: 3000 });Another extension owns window.ethereum
MetaMask has shipped window.ethereum as non-writable. When that happens Qiubit stays reachable through EIP-6963 and through its own window.octra key, and the SDK checks all three, including the window.ethereum.providers array.
disconnect() does not revoke anything
The extension owns the permission; a page cannot revoke it. disconnect() clears local state only. To actually revoke, the person removes the site in the wallet's connections screen.
Ship it against a real wallet
The example dApp in the repo walks every connection path, with the failure states left visible rather than hidden.