
Cherry Miniapp Integration
by cherry.fun
Install this Skill
Security scan
Not scanned yet
@cherrydotfun/miniapp-sdk
SDK for building mini-apps embedded in Cherry messenger. Provides wallet integration, user/room context, and navigation — works in both WebView (mobile) and iframe (web).
Supports both @solana/web3.js (legacy wallet-adapter) and @solana/kit (modern TransactionSigner).
Install
npm install @cherrydotfun/miniapp-sdk
Peer dependencies — install only what you need:
# For @solana/web3.js (legacy wallet-adapter)
npm …
@cherrydotfun/miniapp-sdk
SDK for building mini-apps embedded in Cherry messenger. Provides wallet integration, user/room context, and navigation — works in both WebView (mobile) and iframe (web).
Supports both @solana/web3.js (legacy wallet-adapter) and @solana/kit (modern TransactionSigner).
Install
npm install @cherrydotfun/miniapp-sdk
Peer dependencies — install only what you need:
# For @solana/web3.js (legacy wallet-adapter)
npm install @solana/wallet-adapter-base @solana/web3.js
# For @solana/kit (modern)
npm install @solana/signers
# For React hooks
npm install react
Package Exports
| Entry Point | Description | Solana Dependency |
|---|---|---|
@cherrydotfun/miniapp-sdk | Core client, bridge, env detection, token verification | None |
@cherrydotfun/miniapp-sdk/react | React provider and hooks | None |
@cherrydotfun/miniapp-sdk/solana | CherryWalletAdapter for wallet-adapter ecosystem | @solana/web3.js + @solana/wallet-adapter-base |
@cherrydotfun/miniapp-sdk/kit | TransactionSigner for @solana/kit | None (structural typing) |
Quick Start — @solana/web3.js
import { CherryMiniAppProvider, useCherryMiniApp, useCherryWallet } from '@cherrydotfun/miniapp-sdk/react';
import { CherryWalletAdapter } from '@cherrydotfun/miniapp-sdk/solana';
// Drop-in for @solana/wallet-adapter-react
const wallets = [new CherryWalletAdapter()];
function MyGame() {
const { user, room, launchToken, isReady } = useCherryMiniApp();
const { publicKey, signTransaction, signAllTransactions, signMessage } = useCherryWallet();
if (!isReady) return Loading...;
return (
<p>Welcome, {user.displayName}!</p>
<p>Room: {room.title} ({room.memberCount} members)</p>
);
}
Quick Start — @solana/kit
import { CherryMiniApp } from '@cherrydotfun/miniapp-sdk';
import { createCherrySigner } from '@cherrydotfun/miniapp-sdk/kit';
const cherry = new CherryMiniApp();
await cherry.init();
// TransactionSigner — use with @solana/kit transaction builders
const signer = createCherrySigner(cherry);
// Sign transactions
const [signed] = await signer.signTransactions([{ messageBytes, signatures: {} }]);
// Sign messages
const [signature] = await signer.signMessages([messageBytes]);
Quick Start — React + Kit
import { CherryMiniAppProvider, useCherryApp } from '@cherrydotfun/miniapp-sdk/react';
import { createCherrySigner } from '@cherrydotfun/miniapp-sdk/kit';
function MyGame() {
const app = useCherryApp(); // CherryMiniApp instance
const handleSign = async () => {
const signer = createCherrySigner(app);
const [signed] = await signer.signTransactions([{ messageBytes, signatures: {} }]);
};
}
Environment Detection
Check if running inside Cherry before initializing:
import { isInsideCherry, getCherryEnvironment } from '@cherrydotfun/miniapp-sdk';
if (isInsideCherry()) {
// Running inside Cherry — SDK will work
} else {
// Standalone — show regular wallet connect
}
const env = getCherryEnvironment();
// env.platform: 'webview' | 'iframe' | 'standalone'
// env.isEmbedded: boolean
React hook (no provider needed):
import { useCherryEnvironment } from '@cherrydotfun/miniapp-sdk/react';
function App() {
const { isEmbedded, platform } = useCherryEnvironment();
if (!isEmbedded) return ;
return ;
}
Strict Mode
By default the SDK uses fallback heuristics for backward compatibility with older Cherry builds:
ReactNativeWebView— present in any React Native WebView, not just Cherry'swindow.parent !== window— true inside any iframe, not just Cherry's
This can cause false positives (e.g. wallet in-app browsers). Once your users are on a Cherry version that injects window.__cherry (WebView) or appends cherry_embed=1 (iframe), enable strict mode to rely only on Cherry-specific signals:
// Standalone functions
isInsideCherry({ strict: true });
getCherryEnvironment({ strict: true });
detectPlatform({ strict: true });
// React hook
const { isEmbedded } = useCherryEnvironment({ strict: true });
// Provider (passes strict to CherryMiniApp internally)
...
// CherryMiniApp
const cherry = new CherryMiniApp({ strict: true });
In strict mode only these signals are accepted:
- Mobile WebView:
window.__cherry === true(injected by Cherry before page load) - Web iframe:
cherry_embed=1query parameter (appended by Cherry host to the URL)
Web Embedding — CORS & CSP
When your mini-app runs inside the Cherry web client (iframe), the browser enforces standard cross-origin policies. Two things must be configured on your mini-app's server for the embed to work.
1. Allow Cherry to frame your app (frame-ancestors)
By default many frameworks set X-Frame-Options: SAMEORIGIN or a restrictive Content-Security-Policy, which blocks any iframe embedding. You need to explicitly allow Cherry's origin.
Option A — CSP header (recommended):
Content-Security-Policy: frame-ancestors 'self' https://chat.cherry.fun
Option B — X-Frame-Options (legacy, less flexible):
X-Frame-Options: ALLOW-FROM https://chat.cherry.fun
> X-Frame-Options: ALLOW-FROM is ignored by Chrome/Firefox — prefer the CSP header.
Framework examples:
// Next.js — next.config.ts
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: "frame-ancestors 'self' https://chat.cherry.fun",
},
],
},
];
},
};
// Express / Node.js
app.use((req, res, next) => {
res.setHeader('Content-Security-Policy', "frame-ancestors 'self' https://chat.cherry.fun");
next();
});
# Nginx
add_header Content-Security-Policy "frame-ancestors 'self' https://chat.cherry.fun" always;
2. CORS on your backend API
When your mini-app frontend (served from https://yourgame.example) calls its own backend API, the browser sends the request with Origin: https://yourgame.example — same as outside the iframe, so no additional CORS config is needed if your API already allows that origin.
The one case that requires attention: if your API validates the Referer header or only allows requests when Origin exactly matches a whitelist, make sure https://yourgame.example is in that list. The Cherry host page is never the origin of your API calls — the iframe is its own browsing context.
If your mini-app calls any Cherry API endpoints directly (not via the SDK bridge), add the appropriate Access-Control-Allow-Origin on your side or proxy through your own backend.
Checklist
| Requirement | |
|---|---|
| ✅ | Content-Security-Policy: frame-ancestors … https://chat.cherry.fun on all HTML responses |
| ✅ | No X-Frame-Options: DENY or X-Frame-Options: SAMEORIGIN without override |
| ✅ | Backend API allows Origin: https://yourgame.example (usually already true) |
| ✅ | No Referer-based origin checks that would break inside an iframe |
Navigation
Open Cherry screens from your mini-app:
import { useCherryNavigate } from '@cherrydotfun/miniapp-sdk/react';
function MyComponent() {
const navigate = useCherryNavigate();
// Open user profile — accepts wallet address, domain, or @handle
await navigate.userProfile('alice.sol');
await navigate.userProfile('@alice');
// Open room — accepts roomId or @handle
await navigate.openRoom('@solminer');
await navigate.openRoom('roomId123');
}
Launch Token (Backend Verification)
The SDK provides a JWT launch token signed by Cherry's server. Verify it on your backend:
import { verifyLaunchToken } from '@cherrydotfun/miniapp-sdk';
const payload = await verifyLaunchToken(token, {
expectedAppId: 'your-app-id',
// jwksUrl defaults to https://chat.cherry.fun/.well-known/jwks.json
});
// payload.sub — wallet address
// payload.room_id — room where app was opened
// payload.user — { display_name, avatar_url }
// payload.room — { title, member_count }
Vanilla JS (No React)
import { CherryMiniApp } from '@cherrydotfun/miniapp-sdk';
const cherry = new CherryMiniApp();
await cherry.init();
cherry.user.publicKey; // wallet address
cherry.room.title; // room name
cherry.launchToken; // JWT for backend
const sig = await cherry.wallet.signMessage(new TextEncoder().encode('hello'));
const signed = await cherry.wallet.signAllTransactions([tx1, tx2, tx3]); // batch sign
await cherry.navigate.userProfile('alice.sol');
cherry.on('suspended', () => console.log('App suspended'));
cherry.on('resumed', () => console.log('App resumed'));
API Reference
React Hooks
| Hook | Description |
|---|---|
useCherryMiniApp() | { user, room, launchToken, isReady, error } |
useCherryApp() | CherryMiniApp instance (for kit signer etc.) |
useCherryWallet() | { publicKey, connected, signTransaction, signAllTransactions, signMessage, signAndSendTransaction } |
useCherryNavigate() | { userProfile(id), openRoom(id) } |
useCherryEnvironment(opts?) | { isEmbedded, platform } — no provider needed; pass { strict: true } to disable fallbacks |
CherryMiniApp (Core)
| Property/Method | Description |
|---|---|
new CherryMiniApp(opts?) | opts.initTimeout (ms, default 10 000); opts.strict (disable fallback detection) |
init() | Wait for Cherry host handshake |
user | { publicKey, displayName, avatarUrl } |
room | { id, title, memberCount } |
launchToken | JWT string for backend verification |
wallet.signTransaction(tx) | Sign a transaction (returns Uint8Array) |
wallet.signAllTransactions(txs) | Sign multiple transactions in a single batch (returns Uint8Array[]) |
wallet.signMessage(msg) | Sign an arbitrary message |
wallet.signAndSendTransaction(tx) | Sign and submit transaction |
navigate.userProfile(id) | Open user profile (wallet/domain/@handle) |
navigate.openRoom(id) | Open room (roomId/@handle) |
on(event, handler) | Listen to suspended, resumed, walletDisconnected |
destroy() | Cleanup listeners |
CherryWalletAdapter (solana/)
import { CherryWalletAdapter } from '@cherrydotfun/miniapp-sdk/solana';
Drop-in BaseWalletAdapter for @solana/wallet-adapter-react. Handles connect, signTransaction, signAllTransactions, signMessage, sendTransaction.
createCherrySigner (kit/)
import { createCherrySigner } from '@cherrydotfun/miniapp-sdk/kit';
Returns a TransactionSigner compatible with @solana/kit. Supports signTransactions and signMessages.
Bridge Protocol
The SDK communicates with Cherry via postMessage. The protocol is versioned (v2) and uses JWT launch tokens for authentication.
| Message | Direction | Description |
|---|---|---|
cherry:init | Host → App | Handshake with JWT token |
cherry:ready | App → Host | App acknowledges init |
cherry:request | App → Host | Wallet/navigate operations |
cherry:response | Host → App | Operation result |
cherry:event | Host → App | Lifecycle events |
Privy Integration
If your mini-app uses Privy for authentication or embedded wallets, you can use Cherry's launch token as a custom auth provider — giving users zero-click login inside Cherry.
Setup
-
Privy Dashboard → Settings → Custom Auth → Add Provider:
- JWKS URL:
https://chat.cherry.fun/.well-known/jwks.json - Issuer:
https://chat.cherry.fun - User ID field:
sub
- JWKS URL:
-
Code — dual-mode login (Cherry + standalone):
import { CherryMiniAppProvider, useCherryApp, useCherryEnvironment } from '@cherrydotfun/miniapp-sdk/react';
import { usePrivy } from '@privy-io/react-auth';
function AuthGate({ children }) {
const { isEmbedded } = useCherryEnvironment();
const cherry = useCherryApp();
const { loginWithCustomAccessToken, authenticated, ready } = usePrivy();
useEffect(() => {
if (!ready || authenticated) return;
if (isEmbedded && cherry?.launchToken) {
loginWithCustomAccessToken(cherry.launchToken); // transparent login
}
}, [ready, authenticated, isEmbedded, cherry]);
if (!authenticated && !isEmbedded) return ;
return <>{children}</>;
}
- Helper — get auth config programmatically:
import { getCherryCustomAuthConfig } from '@cherrydotfun/miniapp-sdk';
const { token, jwksUrl, issuer } = getCherryCustomAuthConfig(cherry);
| Environment | Login Method | User Action |
|---|---|---|
| Inside Cherry | loginWithCustomAccessToken(launchToken) | None — automatic |
| Standalone | Standard Privy UI (email, social, wallet) | User clicks login |
See the integration skill for a complete step-by-step guide.
AI-Assisted Integration
This package includes a Claude Code / Codex skill that automates SDK integration into existing web3 apps. After installing the SDK, copy the skill to your AI assistant and say "Integrate Cherry Mini-App SDK" — it will analyze your codebase and guide you step by step.
License
MIT

Cherry Miniapp Integration
Install this Skill

