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-sdk bridges both sides using postMessage.

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(): boolean

  • onAppData(fn: (data: string) => void): void

  • onWalletChange(fn: (address: string | null) => void): () => void

  • sendTransactions(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:

  1. disconnected

  2. connecting (optional UI state)

  3. connected

  4. error (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:

  1. Validate input

  2. Normalize addresses/amounts

  3. Optional preflight simulation

  4. Build one or more tx payloads

  5. Show pending UI

  6. Submit via host bridge

  7. Wait for confirmations (if required by UX)

  8. Show success/failure

  9. Refresh affected data

Example shell:

8. Signing Flows (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)

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 checks

  • amount: decimal precision + non-negative + max bounds

  • url: 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_error

  • user_rejected

  • network_error

  • host_bridge_error

  • unexpected_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?