P-1114 Add Formo + Turnkey embedded wallet integration example#7
P-1114 Add Formo + Turnkey embedded wallet integration example#7
Conversation
Adds a new example demonstrating Formo Analytics SDK integration with Turnkey embedded wallets. Includes a custom wagmi connector wrapping @turnkey/viem for signing, TurnkeyProvider for passkey auth, and FormoAnalyticsProvider for automatic wallet event tracking. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces a new example application that demonstrates the powerful synergy between Formo Analytics and Turnkey embedded wallets. It provides a practical, working model for developers to understand how to integrate passkey-based authentication with web3 wallet interactions, ensuring that all relevant wallet activities are automatically captured for analytics purposes. The example highlights a robust architecture for modern web3 applications, focusing on user experience and data insights. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive example application for integrating Formo Analytics with Turnkey embedded wallets. The code is well-structured and provides a clear demonstration of the integration. I've identified a couple of minor issues on the main page: an unused variable and an incorrect GitHub link in the footer. My review includes suggestions to address these points for improved code quality and correctness.
with-turnkey/src/app/page.tsx
Outdated
| }); | ||
|
|
||
| // Register the connector with wagmi and connect | ||
| const augmentedConfig = wagmiConfig; |
with-turnkey/src/app/page.tsx
Outdated
| </p> | ||
| <p> | ||
| <a | ||
| href="https://github.com/getformo/formo-example-turnkey" |
There was a problem hiding this comment.
The "View on GitHub" link points to a non-existent repository. Since this example is part of the getformo/examples repository, the link should be updated to point to the correct directory within that repository.
| href="https://github.com/getformo/formo-example-turnkey" | |
| href="https://github.com/getformo/examples/tree/main/with-turnkey" |
| async getProvider(): Promise<EIP1193Provider> { | ||
| // Return a minimal EIP-1193 provider that delegates to viem | ||
| // For most wagmi usage the connector methods above are sufficient | ||
| return undefined as unknown as EIP1193Provider; | ||
| }, |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
- Implement proper EIP-1193 provider in turnkey-connector.ts that handles personal_sign, eth_signTypedData_v4, eth_sendTransaction, and forwards unknown RPC calls to the chain's public endpoint - Remove unused augmentedConfig variable and wagmiConfig import - Fix GitHub link to point to getformo/examples/tree/main/with-turnkey https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
- Update @turnkey/sdk-browser to ^5.15.2, @turnkey/sdk-react to ^5.5.6, @turnkey/viem to ^0.14.26 (^3.0.0 doesn't exist on npm) - Replace getCurrentUser() with getSession() (v5 API change) - Replace getActiveClient() with client from useTurnkey() (v5 API change) - Fix wagmi v3 connect() withCapabilities type compatibility - Fix viem TransactionSerializable union type compatibility https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
The provider returned by getProvider() was missing on(), removeListener(), and emit() methods required by the EIP-1193 standard. Wagmi calls these to subscribe to accountsChanged and other provider events, causing a TypeError at runtime. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Previously each getProvider() call created a new provider with fresh event listeners, causing wagmi's event subscriptions to be silently lost. Now the provider and its listeners map are cached at the connector level and cleared on disconnect. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Rewrites the connector to use Turnkey's official EIP-1193 provider package, following the approach from docs.turnkey.com/wallets/wagmi. This eliminates ~150 lines of hand-rolled provider code (signing, transaction building, gas estimation, nonce management, RPC forwarding, event emitter) and replaces it with the battle-tested @turnkey/eip-1193-provider which handles all of this out of the box. Changes: - Replace @turnkey/viem with @turnkey/eip-1193-provider - Connector now accepts walletId instead of signWith address - Add switchChain support via wallet_switchEthereumChain - Remove manual wallet account fetching from page.tsx - Update README to reference the official Turnkey wagmi docs https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
The fire-and-forget connect() mutate function doesn't throw on failure, so the existing try/catch never caught wagmi connection errors and setTurnkeyUser ran before connection was confirmed. Switching to connectAsync ensures errors propagate to the catch block and setTurnkeyUser only runs on successful connection. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Formo's autocapture (enabled in providers.tsx) already monitors wagmi state changes and fires disconnect events. The manual useEffect bridge watching isConnected caused duplicate disconnect events. Removed the bridge and the now-unused prevAuthRef/useRef import. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive example application for integrating Formo with Turnkey embedded wallets. The code is well-structured and includes good documentation. My review identifies a critical bug in the custom turnkeyConnector where it fails to correctly extract the EIP-1193 provider, which would prevent the connector from functioning. I've also included suggestions to improve the display of wallet balances to avoid precision loss, to refactor the main page component for better maintainability, and to reconsider the use of force-dynamic which has performance implications. Addressing these points will make the example more robust and a better reference for developers.
| const provider = await createEIP1193Provider({ | ||
| walletId: walletId as UUID, | ||
| organizationId: organizationId as UUID, | ||
| turnkeyClient: client, | ||
| chains, | ||
| }); | ||
|
|
||
| cachedProvider = provider as unknown as EIP1193Provider; | ||
| return cachedProvider; |
There was a problem hiding this comment.
The createEIP1193Provider function from Turnkey returns an object containing the provider on the eip1193Provider property, not the provider itself. The current code incorrectly assigns the entire returned object to cachedProvider. This will cause runtime errors when methods like request are called on it. You should destructure eip1193Provider from the result, as shown in the official Turnkey documentation.
| const provider = await createEIP1193Provider({ | |
| walletId: walletId as UUID, | |
| organizationId: organizationId as UUID, | |
| turnkeyClient: client, | |
| chains, | |
| }); | |
| cachedProvider = provider as unknown as EIP1193Provider; | |
| return cachedProvider; | |
| const { eip1193Provider } = await createEIP1193Provider({ | |
| walletId: walletId as UUID, | |
| organizationId: organizationId as UUID, | |
| turnkeyClient: client, | |
| chains, | |
| }); | |
| cachedProvider = eip1193Provider as unknown as EIP1193Provider; | |
| return cachedProvider; |
with-turnkey/src/app/layout.tsx
Outdated
| import { Providers } from "./providers"; | ||
| import "./globals.css"; | ||
|
|
||
| export const dynamic = "force-dynamic"; |
There was a problem hiding this comment.
Using export const dynamic = "force-dynamic"; opts the entire route out of caching and forces it to be dynamically rendered on the server for every request. While this might be acceptable for a demo, it has significant performance implications for a production application. Since the main page is a client component ("use client" in page.tsx), the primary interactions are client-side. You might not need to disable all server-side caching so aggressively. Please consider if this is truly necessary or if a more granular caching strategy (or removing this line to use Next.js defaults) would be more appropriate for a real-world scenario this example might inspire.
|
|
||
| import { turnkeyConnector } from "@/config/turnkey-connector"; | ||
|
|
||
| export default function Home() { |
There was a problem hiding this comment.
This Home component is over 400 lines long, handling state, logic, and rendering for multiple distinct sections (authentication, wallet status, analytics actions, etc.). For better maintainability and reusability, consider breaking it down into smaller, more focused components. For example, WalletConnection, WalletStatus, and AnalyticsActions could each be their own component. While this is an example, demonstrating good component structure would be beneficial for developers using this as a reference.
with-turnkey/src/app/page.tsx
Outdated
| ? `${parseFloat( | ||
| formatUnits(balance.value, balance.decimals) | ||
| ).toFixed(4)} ${balance.symbol}` |
There was a problem hiding this comment.
Using parseFloat on values representing cryptocurrency amounts is risky as it can lead to loss of precision due to floating-point arithmetic limitations. viem's formatUnits correctly returns a string to maintain precision. It's safer to perform string manipulation to truncate the value for display purposes, rather than converting to a floating-point number.
For example:
const formatted = formatUnits(balance.value, balance.decimals);
const dotIndex = formatted.indexOf('.');
const displayValue = dotIndex === -1 ? formatted : formatted.slice(0, dotIndex + 5); // 1 for dot, 4 for decimals
// ... then use displayValue- Use string slicing instead of parseFloat for balance display to avoid floating-point precision loss with cryptocurrency amounts - Remove force-dynamic from layout.tsx since client components don't need server-side dynamic rendering forced on every request https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a comprehensive example for integrating Formo with Turnkey embedded wallets. The code is well-structured and the example is feature-rich. I've found a critical issue regarding a server-side module being imported in client code, which will cause a runtime error. I've also included a couple of medium-severity suggestions to improve documentation accuracy and code readability. Overall, great work on this example application.
| import { getAddress, type Address, type EIP1193Provider } from "viem"; | ||
| import type { TurnkeyBrowserClient } from "@turnkey/sdk-browser"; | ||
| import type { AddEthereumChainParameter } from "viem"; | ||
| import type { UUID } from "crypto"; |
There was a problem hiding this comment.
The crypto module is a Node.js built-in and is not available in the browser. Importing UUID from it will cause a runtime error in this client-side code (due to 'use client'). To resolve this, you can replace the import with a local type alias for UUID, as the underlying type expected by the Turnkey SDK is a string.
| import type { UUID } from "crypto"; | |
| type UUID = string; |
with-turnkey/src/app/page.tsx
Outdated
| ? `${(() => { | ||
| const formatted = formatUnits(balance.value, balance.decimals); | ||
| const dot = formatted.indexOf("."); | ||
| return dot === -1 ? formatted : formatted.slice(0, dot + 5); | ||
| })()} ${balance.symbol}` |
There was a problem hiding this comment.
This immediately-invoked function expression (IIFE) for formatting the balance adds complexity within the JSX, making it harder to read and maintain. It's better to extract this logic into a helper function for improved clarity and separation of concerns.
For example, you could define a helper function:
const formatDisplayBalance = (balance: { value: bigint; decimals: number; }) => {
const formatted = formatUnits(balance.value, balance.decimals);
const dotIndex = formatted.indexOf('.');
// Truncate to 4 decimal places without rounding
if (dotIndex === -1) {
return formatted;
}
return formatted.slice(0, dotIndex + 5);
};And then use it in your component like this:
{balance ? `${formatDisplayBalance(balance)} ${balance.symbol}` : "Loading..."}
with-turnkey/README.md
Outdated
| - `wagmiConfig.subscribe()` for wallet connect/disconnect/chain events | ||
| - `queryClient.getMutationCache().subscribe()` for signature and transaction events | ||
|
|
||
| **Note:** Turnkey's logout doesn't trigger wagmi's disconnect status, so the demo includes a `useEffect` that manually tracks disconnect events when the connection state changes. |
There was a problem hiding this comment.
This note mentions a useEffect for manually tracking disconnect events when the connection state changes. However, the implementation in page.tsx only shows a handleDisconnect function that is manually triggered by a button click. This part of the documentation seems to be inconsistent with the code. Please update the README to accurately describe the implemented disconnect behavior.
…ADME
- Replace `import type { UUID } from "crypto"` with a local type alias
to avoid importing from a Node.js built-in in client-side code
- Extract IIFE balance formatting into a named helper function
- Remove stale README note about manual disconnect useEffect bridge
https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a comprehensive example for integrating Formo with Turnkey embedded wallets. The implementation is well-structured, with a clear separation of concerns between UI, configuration, and the custom wagmi connector. The code is generally of high quality. I've pointed out a couple of areas for improvement in page.tsx related to handling side effects and ensuring robust state management for user sessions.
| return dot === -1 ? formatted : formatted.slice(0, dot + 5); | ||
| } | ||
|
|
||
| export default function Home() { |
There was a problem hiding this comment.
The pull request description mentions: "Includes a useEffect hook to manually track Turnkey logout events since Turnkey's logout doesn't trigger wagmi's disconnect status". However, this functionality does not appear to be implemented in the current code. Without it, the application's state could become inconsistent if a user logs out of Turnkey through other means (e.g., another browser tab). Consider implementing a mechanism, perhaps using useEffect to poll turnkey.getSession(), to detect external logout events and synchronize the application state.
with-turnkey/src/app/page.tsx
Outdated
| }); | ||
|
|
||
| setCustomEventSent(true); | ||
| setTimeout(() => setCustomEventSent(false), 2000); |
There was a problem hiding this comment.
Using setTimeout without a cleanup function can lead to memory leaks and attempts to set state on an unmounted component if the component unmounts before the timeout completes. It's a best practice to manage this kind of side effect within a useEffect hook, which provides a cleanup mechanism.
Consider removing this line and adding a useEffect hook to manage the state reset for customEventSent:
useEffect(() => {
if (customEventSent) {
const timer = setTimeout(() => {
setCustomEventSent(false);
}, 2000);
return () => clearTimeout(timer);
}
}, [customEventSent]);|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The bare setTimeout in handleTrackCustomEvent could attempt to set state on an unmounted component. Moving it to a useEffect with a cleanup function ensures the timer is cleared if the component unmounts. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a well-crafted example application demonstrating the integration of Formo Analytics with Turnkey embedded wallets. The implementation is thorough, covering authentication, wallet interactions via a custom wagmi connector, and both automatic and manual analytics event tracking. The code is of high quality, with robust error handling and clear separation of concerns in the provider setup. The accompanying documentation is comprehensive. My review includes one minor suggestion to enhance the clarity of the setup instructions in the README file.
with-turnkey/README.md
Outdated
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js 18+ or pnpm 9.15+ |
There was a problem hiding this comment.
The use of 'or' in the prerequisites is slightly ambiguous. Since both Node.js and a package manager are required for the project setup, using 'and' would provide more clarity. Given that pnpm is specified in package.json, it's the recommended package manager.
| - Node.js 18+ or pnpm 9.15+ | |
| - Node.js 18+ and pnpm 9.15+ |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a well-structured and comprehensive example for integrating Formo with Turnkey embedded wallets. The code is clean, the documentation is excellent, and the UI is well-designed for demonstrating the features. I've found one important issue in the custom wagmi connector implementation that needs to be addressed to ensure correct reactive behavior.
| chains, | ||
| }); | ||
|
|
||
| cachedProvider = provider as unknown as EIP1193Provider; |
There was a problem hiding this comment.
The custom connector is missing event listeners for accountsChanged, chainChanged, and disconnect. Without these, wagmi won't be able to react to state changes that originate outside of this application, such as in another browser tab or wallet interface. You should subscribe to these events on the provider after it's created and forward them to wagmi's emitter.
provider.on("accountsChanged", this.onAccountsChanged);
provider.on("chainChanged", this.onChainChanged);
provider.on("disconnect", this.onDisconnect);
cachedProvider = provider as unknown as EIP1193Provider;Wire up accountsChanged, chainChanged, and disconnect event listeners on the EIP-1193 provider so wagmi can react to state changes originating outside the application (e.g. another browser tab). https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Without cleanup, if the Turnkey provider is kept alive by internal SDK references, old listeners could fire and interfere with subsequent connections via the shared wagmi config emitter. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a comprehensive example application for integrating Formo with Turnkey embedded wallets. The code is well-structured and provides a clear demonstration of the integration. I've added a few suggestions to improve the documentation clarity and developer experience for those running the example.
with-turnkey/README.md
Outdated
| pnpm install | ||
| # or | ||
| npm install |
There was a problem hiding this comment.
with-turnkey/README.md
Outdated
| pnpm dev | ||
| # or | ||
| npm run dev |
| }; | ||
|
|
||
| export function Providers({ children }: { children: ReactNode }) { | ||
| const formoWriteKey = process.env.NEXT_PUBLIC_FORMO_WRITE_KEY; |
There was a problem hiding this comment.
While the app correctly handles a missing formoWriteKey by not rendering FormoAnalyticsProvider, it does so silently. For a better developer experience in this example app, consider adding a console.warn if formoWriteKey is not set to inform the developer that analytics are disabled. This would be consistent with the explicit error handling for the missing Turnkey Organization ID.
For example:
if (!formoWriteKey) {
console.warn("Missing NEXT_PUBLIC_FORMO_WRITE_KEY. Formo analytics will be disabled.");
}- Remove npm install/dev alternatives from README since package.json specifies pnpm as the package manager - Add console.warn when NEXT_PUBLIC_FORMO_WRITE_KEY is missing so developers know analytics are disabled https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive example application for integrating Formo Analytics with Turnkey embedded wallets. The implementation is well-structured, covering passkey authentication, a custom wagmi connector, and both automatic and manual event tracking. My review focuses on improving code maintainability and consistency by refining how configuration is handled and ensuring documentation accuracy. The changes I've suggested will make the example more robust and easier for other developers to follow.
with-turnkey/README.md
Outdated
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js 18+ and pnpm 9.15+ |
There was a problem hiding this comment.
with-turnkey/src/app/page.tsx
Outdated
| const organizationId = | ||
| process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID ?? ""; |
There was a problem hiding this comment.
Instead of accessing process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID directly within the component, it's better to use the organizationId from the turnkey object provided by the useTurnkey hook. This improves component encapsulation and relies on the configuration provided by the TurnkeyProvider, making the code more maintainable. Since you've already confirmed the turnkey object exists when you check for session, you can safely access turnkey.organizationId.
| const organizationId = | |
| process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID ?? ""; | |
| const organizationId = turnkey.organizationId; |
with-turnkey/src/app/providers.tsx
Outdated
| const turnkeyConfig = { | ||
| apiBaseUrl: "https://api.turnkey.com", | ||
| defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID ?? "", | ||
| rpId: process.env.NEXT_PUBLIC_TURNKEY_RP_ID ?? "localhost", | ||
| iframeUrl: "https://auth.turnkey.com", | ||
| serverSignUrl: process.env.NEXT_PUBLIC_TURNKEY_SERVER_SIGN_URL, | ||
| }; | ||
|
|
||
| export function Providers({ children }: { children: ReactNode }) { | ||
| const formoWriteKey = process.env.NEXT_PUBLIC_FORMO_WRITE_KEY; | ||
| const [queryClient] = useState(() => new QueryClient()); |
There was a problem hiding this comment.
The turnkeyConfig object is defined at the module level and depends on process.env. While this works in Next.js with client components, it's a better practice to define such configuration objects within the component body. This ensures that the configuration is created in the client-side rendering context and avoids potential issues with server-side rendering or bundlers in different environments. I'd suggest moving it inside the Providers component.
| const turnkeyConfig = { | |
| apiBaseUrl: "https://api.turnkey.com", | |
| defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID ?? "", | |
| rpId: process.env.NEXT_PUBLIC_TURNKEY_RP_ID ?? "localhost", | |
| iframeUrl: "https://auth.turnkey.com", | |
| serverSignUrl: process.env.NEXT_PUBLIC_TURNKEY_SERVER_SIGN_URL, | |
| }; | |
| export function Providers({ children }: { children: ReactNode }) { | |
| const formoWriteKey = process.env.NEXT_PUBLIC_FORMO_WRITE_KEY; | |
| const [queryClient] = useState(() => new QueryClient()); | |
| export function Providers({ children }: { children: ReactNode }) { | |
| const turnkeyConfig = { | |
| apiBaseUrl: "https://api.turnkey.com", | |
| defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORGANIZATION_ID ?? "", | |
| rpId: process.env.NEXT_PUBLIC_TURNKEY_RP_ID ?? "localhost", | |
| iframeUrl: "https://auth.turnkey.com", | |
| serverSignUrl: process.env.NEXT_PUBLIC_TURNKEY_SERVER_SIGN_URL, | |
| }; | |
| const formoWriteKey = process.env.NEXT_PUBLIC_FORMO_WRITE_KEY; | |
| const [queryClient] = useState(() => new QueryClient()); |
- Fix pnpm version in README to exact 9.15.4 instead of range - Use turnkey.config.defaultOrganizationId instead of direct env var access - Move turnkeyConfig inside Providers component for proper client-side context https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Previously the login handler only checked for an existing Turnkey session and showed an error if none was found. Now it uses passkeyClient.login() to initiate a passkey-based session, then proceeds with wallet connection. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
| export const wagmiConfig = createConfig({ | ||
| chains: [mainnet, sepolia, polygon, arbitrum, optimism, base], | ||
| transports: { | ||
| [mainnet.id]: http(), | ||
| [sepolia.id]: http(), | ||
| [polygon.id]: http(), | ||
| [arbitrum.id]: http(), | ||
| [optimism.id]: http(), | ||
| [base.id]: http(), | ||
| }, | ||
| }); |
There was a problem hiding this comment.
🟡 Missing ssr: true in wagmi config causes hydration mismatches in Next.js App Router
The wagmi config is created without ssr: true, which is required for Next.js (SSR) apps to prevent hydration mismatches. Wagmi persists connection state to localStorage by default. On the server, wagmi renders with the default disconnected state, but on the client, it reads persisted state from localStorage, causing React hydration errors. Other Next.js examples in this repo (with-porto/src/wagmi.ts:15, with-metamask/wagmi.config.ts:9) correctly set ssr: true along with cookieStorage to handle this.
| export const wagmiConfig = createConfig({ | |
| chains: [mainnet, sepolia, polygon, arbitrum, optimism, base], | |
| transports: { | |
| [mainnet.id]: http(), | |
| [sepolia.id]: http(), | |
| [polygon.id]: http(), | |
| [arbitrum.id]: http(), | |
| [optimism.id]: http(), | |
| [base.id]: http(), | |
| }, | |
| }); | |
| export const wagmiConfig = createConfig({ | |
| chains: [mainnet, sepolia, polygon, arbitrum, optimism, base], | |
| ssr: true, | |
| transports: { | |
| [mainnet.id]: http(), | |
| [sepolia.id]: http(), | |
| [polygon.id]: http(), | |
| [arbitrum.id]: http(), | |
| [optimism.id]: http(), | |
| [base.id]: http(), | |
| }, | |
| }); |
Was this helpful? React with 👍 or 👎 to provide feedback.
New users can now create a sub-organization with a passkey and embedded wallet directly from the demo app. The flow prompts WebAuthn to create a passkey, creates a Turnkey sub-org with an Ethereum wallet, then logs in and connects automatically. Existing users use 'Log In with Passkey'. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
- Add email input field for account creation (Turnkey requires a valid email for root users) - Store sub-org ID in localStorage after account creation so subsequent logins can scope the passkey auth to the correct sub-organization - Use session.organizationId instead of the parent org config for wallet fetching, supporting both parent-org and sub-org users https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
The handleCreateAccount callback captured the initial empty signupEmail value because it was missing from the dependency array. https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS
Summary
This PR adds a complete example application demonstrating integration between Formo Analytics SDK and Turnkey embedded wallets. The example shows how to authenticate users with passkeys, connect to embedded wallets via wagmi, and automatically track wallet events with Formo.
Key Changes
Main demo page (
src/app/page.tsx): Full-featured UI with wallet connection, status display, and event testing capabilitiesCustom wagmi connector (
src/config/turnkey-connector.ts): Wraps Turnkey's@turnkey/viemto integrate with wagmiuseAccount,useSignMessage, etc.)Provider setup (
src/app/providers.tsx): Configures provider nesting orderConfiguration files:
src/config/wagmi.ts: Wagmi config with support for Ethereum, Sepolia, Polygon, Arbitrum, Optimism, and Basesrc/app/layout.tsx: Root layout with metadatasrc/app/globals.css: Tailwind CSS setupnext.config.ts: Next.js configurationpostcss.config.mjs: PostCSS configurationtsconfig.json: TypeScript configurationpackage.json: Dependencies and scripts.env.example: Environment variable templateDocumentation (
README.md): Comprehensive guide covering setup, features, project structure, and integration detailsNotable Implementation Details
autocapture: trueuseEffecthook to manually track Turnkey logout events since Turnkey's logout doesn't trigger wagmi's disconnect status@ts-ignorecomments for@turnkey/sdk-reactwhich is built for React 18https://claude.ai/code/session_012cKNjKPGBnVZQwNDAL3iKS