API reference
@morsel-wallet/adapter 0.2.0
Everything the package exports, with types and defaults. Every prop is optional unless marked otherwise.
Entry points
@morsel-wallet/adapter Everything: the core, the React provider and hooks, and the connect widget. Needs React 18 or 19 with react-dom.
@morsel-wallet/adapter/core The adapters, errors and constants with no React at all. For Svelte, Vue, plain JavaScript or your own UI.
ESM only, with types. @solana/web3.js 1.x is a peer dependency. Nothing touches window during server rendering; the widget mounts on the client through a portal.
WalletProvider
Holds the wallet state for everything below it. Required, once, near the root.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | Your app. | |
adapters | CookieWalletAdapter[] | [new MorselCookieWalletAdapter()] | The wallets you list yourself. Every installed Wallet Standard wallet (Phantom, Backpack, Solflare, ...) is discovered and added after them, without duplicates. |
adapter | CookieWalletAdapter | A single wallet instead of a list. Kept for older code; prefer adapters. | |
autoConnect | boolean | false | Reconnect on load when the user connected before (remembered in localStorage under morsel.wallet.autoConnect). |
onError | (error: Error) => void | Called with every adapter error. |
Wrap the part of your app that opens the widget in WalletModalProvider (no props), and render <WalletModal /> once inside it.
WalletModal
The connect widget: a 360 px card on desktop and a bottom sheet you can drag down on phones. Which path it takes (QR, extension, phone deep link, Morsel's browser) is decided by the device; see how it connects.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'compact' | 'premium' | 'headless' | 'compact' | premium renders the same widget (kept for 0.1 code). headless is a bare, unstyled list. |
theme | 'light' | 'dark' | 'system' | 'auto' | 'system' | system (or auto) follows the OS setting live. |
accentColor | CSS colour | Morsel blue | Same as setting --mw-accent. |
placement | 'center' | 'anchor' | 'center' | anchor drops the widget down from the element that opened it (desktop). Phones always get the bottom sheet. |
title | string | 'Connect a wallet' | Title of the wallet list. |
subtitle | string | A line under the title. | |
logo | string | ReactNode | Your dApp's logo, next to the title of the wallet list. | |
networks | { id, name, icon? }[] | With more than one entry, a small network switch shows on the list. | |
selectedNetworkId | string | The selected entry of networks. | |
onNetworkChange | (id: string) => void | Called when the user switches network. | |
links | { install, ios, android, extension, website } | Morsel install page | Where the Get Morsel surfaces point. Each link falls back to install. |
autoClose | boolean | true | Close a moment after a successful connection. |
autoConnectInMorsel | boolean | true | Inside Morsel's browser, connect without showing the list. |
mobileDeepLink | string | universal browse link | Override the Open in Morsel link on phones. |
connectLabel | string | 'Connect' | Label of the Morsel action. |
installLabel | string | 'Get' | Label for wallets that are not installed. |
closeLabel | string | 'Close' | Accessible name of the close button. |
showPoweredBy | boolean | false | A small Powered by Morsel line under the list. |
onConnectError | (error: Error) => void | Called when a connection attempt fails or is declined. | |
className / overlayClassName | string | Extra classes on the card and the backdrop. | |
zIndex | number | 2147483000 | Stacking order of the widget layer. |
nonce | string | CSP nonce for the injected stylesheet. |
useWalletModal
Open and close the widget from anywhere under WalletModalProvider.
const { open, close, visible } = useWalletModal();
open(); // the wallet list
open({ view: 'get-morsel' }); // the Get Morsel card
open({ anchor: buttonRef.current }); // with placement="anchor" | Member | Type | Description |
|---|---|---|
open(options?) | { view?, anchor? } | Open the widget. view is 'wallets' (default) or 'get-morsel'; anchor is the element to drop from with placement="anchor". Safe to pass straight to onClick. |
close() | Close it. | |
visible | boolean | Whether it is open. |
setVisible(visible) | boolean | Open or close without options. |
options | WalletModalOpenOptions | Options of the current or last open() call. |
Theming
Every class is prefixed mw- and scoped under .mw-scope, so nothing
leaks into your page and your global button or image rules do not leak in. The stylesheet is injected once into <head>; pass nonce for a strict CSP, or ship getMorselConnectCss() yourself. Override any token:
.mw-scope {
--mw-accent: #ff6b00;
--mw-radius: 20px;
--mw-font: 'DM Sans', system-ui, sans-serif;
}
.mw-scope[data-mw-theme='dark'] {
--mw-bg: #0b0b0f;
} --mw-accent--mw-accent-fg--mw-bg--mw-bg-2--mw-bg-3--mw-fg--mw-fg-2--mw-fg-3--mw-line--mw-overlay--mw-success--mw-danger--mw-warn--mw-shadow--mw-radius--mw-font--mw-z
Hooks
All hooks read from the nearest WalletProvider and are fully typed.
useWallet() WalletContextState Everything: publicKey, connected, connecting, readyState, error, adapters, activeAdapter, connect(), disconnect(), selectAdapter(i), signTransaction(), signAllTransactions(), signMessage(), sendTransaction(), on(), off().
useWalletAddress() string | null The connected address in base58.
useWalletStatus() { publicKey, connected, connecting, readyState, wallet } Connection state only.
useWalletConnection() { connect, disconnect, toggleConnection, connected, connecting, disconnecting, pending, error, clearError } Connect and disconnect with their own pending and error state.
useWalletBalance(connection, { autoFetch?, commitment? }) { balance, lamports, loading, error, refetch, clearError } Native balance (COOK on Cookie Chain, SOL on Solana). balance is in whole units, lamports in base units.
useSignMessage() { signMessage, signing, error, clearError } signMessage(bytes) resolves to the signature (Uint8Array).
useSignTransaction() { signTransaction, signAllTransactions, signing, error, clearError } Legacy and versioned transactions.
useSendTransaction() { sendTransaction, sending, error, clearError } sendTransaction(tx, connection, options?) signs, sends and resolves to the signature.
useWalletError() { error, clearError } The last adapter error.
useAutoConnect() { enableAutoConnect, disableAutoConnect, isAutoConnectEnabled } Control reconnect-on-load at runtime.
useWalletEvent(event, listener) void Subscribe to any adapter event; unsubscribes on unmount.
useOnConnect(listener) void listener(publicKey) on every connection.
useOnDisconnect(listener) void listener() on disconnect.
useOnWalletError(listener) void listener(error) on adapter errors.
useWalletModal() WalletModalContextState Open and close the widget from anywhere under WalletModalProvider.
useOptionalWalletModal() WalletModalContextState | null The same, or null outside a WalletModalProvider.
Sending a transaction with the connected wallet:
const { publicKey, sendTransaction } = useWallet();
const tx = new Transaction().add(
SystemProgram.transfer({ fromPubkey: publicKey, toPubkey: recipient, lamports: 1_000_000 }),
);
tx.feePayer = publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
const signature = await sendTransaction(tx, connection); Events
React to connection changes without polling, with the event hooks or the adapter's own emitter.
useOnConnect((publicKey) => track('wallet_connected', publicKey.toBase58()));
useOnDisconnect(() => track('wallet_disconnected'));
useOnWalletError((error) => toast(error.message));
// Or any adapter event by name
useWalletEvent('readyStateChange', (state) => console.log(state)); | Event | Type | Description |
|---|---|---|
connect | (publicKey: PublicKey) | A wallet connected, by any path. |
disconnect | () | |
error | (error: WalletAdapterError) | |
readyStateChange | (state: WalletReadyState) | The extension appeared or went away. |
wcUriChange | (uri: string) | A new pairing QR. |
relayStatusChange | (status: MorselRelayStatus) | The QR pairing moved on. |
MorselCookieWalletAdapter
The Morsel adapter, from either entry point. It looks for the injected provider (the extension or Morsel's
browser) and keeps a QR pairing ready over the relay. WalletProvider creates one for you.
import { MorselCookieWalletAdapter } from '@morsel-wallet/adapter/core';
const morsel = new MorselCookieWalletAdapter();
morsel.on('connect', (publicKey) => console.log('connected', publicKey.toBase58()));
morsel.on('relayStatusChange', (status) => console.log(status)); // 'waiting', 'scanned', 'connected'...
if (morsel.readyState === 'Installed') {
// The Morsel extension, or the page is open inside Morsel's browser
await morsel.connect();
} else {
// Desktop without the extension: draw this as a QR and the Morsel app scans it.
// On a phone, morsel.connect() opens your page inside Morsel instead.
showQr(morsel.wcUri);
morsel.on('wcUriChange', showQr); // a fresh code after it expires
}
// Then sign like any Solana wallet adapter
const signed = await morsel.signTransaction(transaction); Properties
| Property | Type | Description |
|---|---|---|
name | string | 'Morsel Cookie Wallet' (shown as Morsel). |
icon | string | The Morsel mark as a data URI. |
url | string | Morsel's install page. |
publicKey | PublicKey | null | The connected account. |
connected / connecting | boolean | |
readyState | 'Installed' | 'NotDetected' | 'Unsupported' | Installed when the extension or Morsel's browser is on the page, or after a QR pairing. Unsupported on the server. |
wcUri | string | The live morsel://connect pairing URI. Draw it as a QR. |
wcUriCreatedAt | number | When it was minted (ms). A pairing lives for 5 minutes. |
relayStatus | MorselRelayStatus | Where the QR pairing stands; see below. |
supportedTransactionVersions | Set<'legacy' | 0> | Legacy and v0 transactions. |
Methods
| Method | Type | Description |
|---|---|---|
connect() | Promise<void> | Connect through the extension or Morsel's browser. On a phone without either, it opens your page inside Morsel instead. On a desktop without the extension it throws WalletNotFoundError: pair with the QR. |
disconnect() | Promise<void> | Ends the connection. After a QR pairing, a fresh QR is minted for the next one. |
signTransaction(tx) | Promise<Transaction | VersionedTransaction> | Over the relay, a request times out after 60 seconds. |
signAllTransactions(txs) | Promise<(Transaction | VersionedTransaction)[]> | |
signMessage(message) | Promise<{ signature: Uint8Array }> | |
sendTransaction(tx, connection, options?) | Promise<string> | Signs, then sends with connection.sendRawTransaction. |
restartRelaySession() | void | Mint a fresh QR (new session and keys). A live relay connection is left alone. |
refreshProvider() | void | Look for the injected provider again. Runs every second on its own. |
destroy() | void | Stop timers, close the relay and drop listeners. |
Relay status
relayStatus tracks the QR pairing with the Morsel phone app. The relay is end-to-end
encrypted (NaCl box) between the page and the app; the relay server only forwards ciphertext. A QR lives for five
minutes; restartRelaySession() mints a fresh one.
| Value | Description |
|---|---|
'idle' | No relay session (on the server, or after destroy()). |
'waiting' | The QR is live and nobody has scanned it yet. |
'scanned' | The phone scanned it and shows the approval prompt. |
'rejected' | The user declined in the app; a fresh session follows shortly. |
'connected' | Approved; the encrypted channel is live. |
StandardWalletAdapter
Wraps any Wallet Standard wallet that supports a Solana chain, with the same interface as the Morsel adapter. WalletProvider does this for every installed wallet; use it directly when you build your own list.
import { getWallets } from '@wallet-standard/app';
import { StandardWalletAdapter } from '@morsel-wallet/adapter/core';
const adapters = getWallets().get().map((wallet) => new StandardWalletAdapter(wallet)); Errors
Every error is an instance of WalletAdapterError, so one instanceof check catches them all.
| Class | Description |
|---|---|
WalletAdapterError | The base class of every error below. |
WalletNotFoundError | No Morsel provider on this page (see connect()). |
WalletNotConnectedError | Signing before connecting. |
WalletConnectionError | The wallet refused or failed to connect. |
WalletDisconnectionError | |
WalletSignTransactionError | Declined, timed out or failed to sign. |
WalletSignMessageError |
Constants and helpers
| Name | Type | Description |
|---|---|---|
COOKIE_CHAIN | { name, chainId, symbol, rpcUrl, explorerUrl } | Cookie Chain: 'cookie-mainnet', COOK, https://rpc.cookiescan.io, https://cookiescan.io. |
morselBrowseLink(url, ref?) | string | A universal link that opens url inside Morsel's browser. |
MORSEL_RELAY_PAIRING_TTL_MS | number | How long a QR pairing lives: 5 minutes. |
MORSEL_COOKIE_WALLET_NAME / _ICON / _URL | string | Name, mark and install page of the Morsel adapter. |
detectMorselCookieProvider() | MorselCookieProvider | null | The injected Morsel provider, if any. |
getMorselConnectCss() | string | The widget's stylesheet, to ship yourself under a strict CSP. Main entry only. |