Hosted MiniApps
This is a practical, end-to-end technical guide for building miniapps that run inside the Circles miniapp host and communicate through @aboutcircles/miniapp-sdk.
The guide is intentionally generic: it does not assume any specific app domain (groups, treasury, profiles, etc.). Use it as a production blueprint for any miniapp feature set.
1. Mental Model: How Miniapps Run
A Circles-compatible miniapp is a web app rendered in an iframe inside a host application.
Your app owns UI, local state, validation, and business flow.
The host owns wallet session UX and transaction approval.
@aboutcircles/miniapp-sdkbridges both sides usingpostMessage.
This means:
Local browser testing is useful for UI and logic.
End-to-end wallet/transaction testing must happen in the host.
2. SDK Capabilities (Current Surface)
The SDK currently exposes:
isMiniappMode(): booleanonAppData(fn: (data: string) => void): voidonWalletChange(fn: (address: string | null) => void): () => voidsendTransactions(transactions: Transaction[]): Promise<string[]>signMessage(message: string, signatureType?: 'erc1271' | 'raw'): Promise<{ signature: string; verified: boolean }>
Where Transaction is:
3. Project Setup
3.1 Create the app
Add additional Circles SDK packages only if your use case needs them:
3.2 Suggested structure
Keep host bridge logic isolated from domain logic.
4. Bootstrapping the Host Bridge
Create a dedicated bridge module:
Use onWalletChange as the app’s wallet truth source.
5. Wallet Lifecycle Pattern
Implement wallet lifecycle as a finite-state flow:
disconnectedconnecting(optional UI state)connectederror(recoverable or fatal)
Minimal pattern:
Rules:
Always reset account-scoped caches on wallet changes.
Never assume prior in-memory state is still valid after reconnects.
6. Transaction Submission Pattern
Always format and submit transactions through one adapter.
Why this is critical:
Prevents value-encoding inconsistencies.
Keeps write paths auditable.
Makes retries and logging straightforward.
7. Standard Action Pipeline
For every write action (any state-changing on-chain operation), use this pipeline:
Validate input
Normalize addresses/amounts
Optional preflight simulation
Build one or more tx payloads
Show pending UI
Submit via host bridge
Wait for confirmations (if required by UX)
Show success/failure
Refresh affected data
Example shell:
8. Signing Flows (signMessage)
signMessage)Use signMessage when your backend or protocol needs host-backed signatures.
Guidance:
Prefer
'erc1271'unless you explicitly require raw bytes semantics.Persist signature type with the signature payload for verifier correctness.
9. App Data Injection (onAppData)
onAppData)The host can pass app-specific context via query data.
Use cases:
entry context (resource id, mode, source)
feature flags
campaign or referral metadata
Pattern:
Never trust raw host data without validation.
10. Validation and Type Safety
Minimum validation set:
address: checksum normalization, strict format checksamount: decimal precision + non-negative + max boundsurl: protocol allowlist (https://by default)text: length limits and rendering-safe escaping
Keep pure validation helpers in a dedicated module and test them directly.
11. Error Handling and Recovery
Design a predictable error model:
validation_erroruser_rejectednetwork_errorhost_bridge_errorunexpected_error
Normalize unknown errors:
For recoverable host issues:
show actionable guidance
preserve user input where safe
do not silently retry state-changing actions
12. Concurrency and Race Control
Use request IDs for async operations that may race:
Apply to:
debounced search
paginated loads
wallet-dependent initialization
13. Performance Practices
Debounce user-driven remote search.
Cache immutable reference data.
Batch on-chain reads where available.
Defer non-critical UI hydration.
Split vendor chunks in build output.
Avoid heavy blocking work on input/change handlers.
14. Security Checklist
Never hardcode private keys, tokens, or secrets in frontend.
Escape/sanitize all user-controlled HTML output.
Validate all host-injected and URL-provided data.
Re-validate critical preconditions before tx submission.
Protect against accidental double-submission (disable buttons during pending).
Keep domain allowlists for outbound links.
Generic Starter Skeleton
Last updated
Was this helpful?