Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f01b57a
Add with-turnkey example for Formo SDK integration
claude Mar 19, 2026
a4cbac1
Fix PR review issues: EIP-1193 provider, unused var, GitHub link
claude Mar 19, 2026
9c5df1d
Fix Turnkey package versions and update to v5 SDK API
claude Mar 19, 2026
953329c
Add EIP-1193 event emitter methods to Turnkey provider
claude Mar 19, 2026
77af742
Cache EIP-1193 provider instance across getProvider() calls
claude Mar 20, 2026
85b44e1
Replace custom EIP-1193 provider with @turnkey/eip-1193-provider
claude Mar 20, 2026
37e2ec1
Use connectAsync instead of connect to catch wagmi connection errors
claude Mar 20, 2026
a8ae6ed
Remove redundant disconnect bridge — autocapture already handles it
claude Mar 20, 2026
768dca6
Fix balance display precision and remove unnecessary force-dynamic
claude Mar 21, 2026
56fd570
Replace Node.js crypto UUID import, extract balance helper, update RE…
claude Mar 21, 2026
253fab7
Move setTimeout into useEffect for proper cleanup on unmount
claude Mar 22, 2026
bbc59d6
Fix prerequisites wording: Node.js and pnpm are both required
claude Mar 22, 2026
04e374f
Subscribe to provider events for external state changes
claude Mar 22, 2026
8e923fc
Remove event listeners on disconnect to prevent stale emissions
claude Mar 22, 2026
46855d0
Use pnpm exclusively in README, warn on missing Formo write key
claude Mar 22, 2026
ad0b263
Address PR review feedback for with-turnkey example
claude Mar 22, 2026
9552e64
Fix passkey login flow to initiate session when none exists
claude Mar 23, 2026
9c3f8db
Add 'Create New Account' button for new Turnkey users
claude Mar 23, 2026
60a5052
Fix signup email validation and login for sub-org passkeys
claude Mar 23, 2026
23e8799
Fix stale closure: add signupEmail to useCallback deps
claude Mar 23, 2026
386670e
Move sub-org creation to server-side API route
claude Mar 23, 2026
b893cf2
Remove wagmi, use Turnkey EIP-1193 provider directly with Formo
claude Mar 23, 2026
17163a8
Fix stale closure bugs: turnkeyClient undefined after login and chain…
claude Mar 23, 2026
ad3494d
Address PR review feedback: fix docs, validate env vars, use toHex
claude Mar 23, 2026
3e52a81
Memoize formoOptions to prevent unnecessary SDK re-initialization
claude Mar 23, 2026
6ff820f
Address PR review feedback: docs, naming, and type safety
claude Mar 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions with-turnkey/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Turnkey Configuration
# Get your Organization ID from https://app.turnkey.com/
NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID=your-turnkey-organization-id

# Relying Party ID for passkey authentication (use "localhost" for local dev)
NEXT_PUBLIC_TURNKEY_RP_ID=localhost

# Server-side Turnkey API keys (required for sub-org creation)
# Create an API key in your Turnkey dashboard under "API Keys"
TURNKEY_API_PUBLIC_KEY=your-turnkey-api-public-key
TURNKEY_API_PRIVATE_KEY=your-turnkey-api-private-key

# Formo Configuration
# Get your Write Key from https://app.formo.so/
NEXT_PUBLIC_FORMO_WRITE_KEY=your-formo-write-key
161 changes: 161 additions & 0 deletions with-turnkey/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Formo + Turnkey Example App

This is an example Next.js application demonstrating integration between [Turnkey](https://www.turnkey.com/) embedded wallets and the [Formo Analytics SDK](https://formo.so/).

## Features

- **Turnkey Authentication**: Login with passkeys to access embedded wallets — no browser extensions needed
- **Formo Analytics Integration**: Track wallet events and custom analytics
- **EIP-1193 Provider**: Direct integration with Turnkey's `@turnkey/eip-1193-provider` for signing and transactions
- **Event Testing UI**: Test all major Formo SDK event types:
- Page events
- Connect/Disconnect events (auto-captured)
- Signature events (auto-captured)
- Transaction events (auto-captured)
- Custom events

## Prerequisites

- Node.js 18+ and pnpm 9.15.4
- A [Turnkey](https://app.turnkey.com/) account and Organization ID
- A [Formo](https://app.formo.so/) account and Write Key

## Quick Start

1. **Clone the repository**

```bash
git clone https://github.com/getformo/examples.git
cd examples/with-turnkey
```

2. **Install dependencies**

```bash
pnpm install
```

3. **Configure environment variables**

Copy the example environment file and add your credentials:

```bash
cp .env.example .env
```

Edit `.env` with your values:

```env
NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID=your-turnkey-organization-id
NEXT_PUBLIC_TURNKEY_RP_ID=localhost
TURNKEY_API_PUBLIC_KEY=your-turnkey-api-public-key
TURNKEY_API_PRIVATE_KEY=your-turnkey-api-private-key
NEXT_PUBLIC_FORMO_WRITE_KEY=your-formo-write-key
```
Comment on lines +48 to +54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The environment variable setup instructions are incomplete. The create-sub-org functionality requires server-side Turnkey API keys (TURNKEY_API_PUBLIC_KEY and TURNKEY_API_PRIVATE_KEY), but they are not mentioned here. Please add them to this section to ensure users can set up the example correctly.

Here is the complete set of variables that should be in the .env file:

NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID=your-turnkey-organization-id
TURNKEY_API_PUBLIC_KEY=your-turnkey-api-public-key
TURNKEY_API_PRIVATE_KEY=your-turnkey-api-private-key
NEXT_PUBLIC_FORMO_WRITE_KEY=your-formo-write-key

Comment on lines +48 to +54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The environment variable example in the Quick Start section is missing NEXT_PUBLIC_TURNKEY_RP_ID, which is present in the .env.example file. This inconsistency could lead to confusion and setup issues for users following the guide. Please update the documentation to include it.

Suggested change
```env
NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID=your-turnkey-organization-id
TURNKEY_API_PUBLIC_KEY=your-turnkey-api-public-key
TURNKEY_API_PRIVATE_KEY=your-turnkey-api-private-key
NEXT_PUBLIC_FORMO_WRITE_KEY=your-formo-write-key
```
```env
NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID=your-turnkey-organization-id
NEXT_PUBLIC_TURNKEY_RP_ID=localhost
TURNKEY_API_PUBLIC_KEY=your-turnkey-api-public-key
TURNKEY_API_PRIVATE_KEY=your-turnkey-api-private-key
NEXT_PUBLIC_FORMO_WRITE_KEY=your-formo-write-key


4. **Run the development server**

```bash
pnpm dev
```

5. Open [http://localhost:3000](http://localhost:3000) in your browser

## Project Structure

```
src/
├── app/
│ ├── api/
│ │ └── create-sub-org/
│ │ └── route.ts # Server-side Turnkey sub-organization creation
│ ├── globals.css # Global styles with Tailwind
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Main demo page with wallet UI and event testing
│ └── providers.tsx # Turnkey and Formo providers
```

## How It Works

### Turnkey Authentication

Turnkey provides embedded wallets secured by passkeys. When a user authenticates:

1. The user creates a passkey and a Turnkey sub-organization is created via the server-side API route
2. The user authenticates via passkeys using `@turnkey/sdk-react`
3. The app fetches the user's wallets from the Turnkey API
4. An EIP-1193 provider is created via `@turnkey/eip-1193-provider`, enabling signing, transactions, and chain switching

### Formo Analytics

The Formo SDK automatically captures wallet events when `autocapture: true` is enabled:

| Event | Description | Auto-captured |
|-------|-------------|---------------|
| `page` | Page view events | Yes |
| `connect` | Wallet connected | Yes |
| `disconnect` | Wallet disconnected | Yes |
| `signature` | Message signing (personal_sign, signTypedData) | Yes |
| `transaction` | Transaction events (eth_sendTransaction) | Yes |
| `chain` | Chain/network changes | Yes |
| `track` | Custom events | No (manual) |

### Manual Event Tracking

```typescript
import { useFormo } from "@formo/analytics";

function MyComponent() {
const formo = useFormo();

// Track custom event
await formo.track("button_clicked", {
button_id: "cta-main",
page: "home"
});

// Track page event
await formo.page("category", "page-name", {
custom_property: "value"
});
}
```

## Provider Setup

The key setup in `providers.tsx`:

```typescript
import { TurnkeyProvider } from "@turnkey/sdk-react";
import { FormoAnalyticsProvider } from "@formo/analytics";

// Provider nesting order:
// TurnkeyProvider > WalletContext > FormoAnalyticsProvider
<FormoAnalyticsProvider
writeKey={formoWriteKey}
options={{
provider, // EIP-1193 provider from Turnkey
autocapture: true,
tracking: true,
}}
>
{children}
</FormoAnalyticsProvider>
```

The Formo SDK hooks into the EIP-1193 provider to auto-capture wallet connect/disconnect, chain changes, signatures, and transactions.

## Supported Chains

The app is configured for Sepolia (testnet). To support additional chains, you would need to update the `chainParam` configuration in `page.tsx` and add a chain-switching UI along with dynamic provider configuration.

## Resources

- [Turnkey Documentation](https://docs.turnkey.com/)
- [Turnkey Embedded Wallet Quickstart](https://docs.turnkey.com/getting-started/embedded-wallet-quickstart)
- [Formo Documentation](https://docs.formo.so/)
- [Formo SDK Installation](https://docs.formo.so/install)

## License

MIT
7 changes: 7 additions & 0 deletions with-turnkey/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
reactStrictMode: true,
};

export default nextConfig;
37 changes: 37 additions & 0 deletions with-turnkey/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "formo-example-turnkey",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formo/analytics": "^1.28.2",
"@heroicons/react": "^2.2.0",
"@turnkey/eip-1193-provider": "^3.4.26",
"@turnkey/sdk-browser": "^5.15.2",
"@turnkey/sdk-react": "^5.5.6",
"@turnkey/sdk-server": "^5.1.1",
"next": "^15.5.9",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-toastify": "^11.0.5",
"viem": "^2.44.4"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@tailwindcss/postcss": "^4.1.18",
"@types/node": "^22.12.0",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3",
"eslint": "^9.18.0",
"eslint-config-next": "^15.5.9",
"postcss": "^8.5.1",
"tailwindcss": "^4.0.0",
"typescript": "^5.7.3"
},
"packageManager": "pnpm@9.15.4"
}
Loading
Loading