skills/embedded-wallet/SKILL.md
Always use this skill when the user asks about embedded wallets, client-side wallets, user-facing wallets, wallet integration in React/React Native/Swift/Unity/JS apps, or non-custodial wallets with Openfort. Covers all client SDKs. Trigger on: "embedded wallet", "client wallet", "user wallet", "@openfort/react", "@openfort/react-native", "OpenfortSwift", "openfort-csharp-unity", "@openfort/openfort-js", "OpenfortProvider", "OpenfortButton", "wallet setup", "wallet integration", "passkey recovery", "gasless transaction client", "auth modal", or any client-side wallet operation with Openfort.
npx skillsauth add openfort-xyz/agent-skills openfort-embedded-walletInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Embedded wallets provide a seamless experience by abstracting away wallet management. Users interact with your app without needing to understand private keys, seed phrases, or blockchain concepts.
Openfort provides embedded wallet SDKs for multiple platforms:
| Platform | Package | Best for |
|----------|---------|----------|
| React / Next.js | @openfort/react | Web apps with pre-built UI modal |
| React Native / Expo | @openfort/react-native | Mobile apps (iOS + Android) |
| iOS / Swift | OpenfortSwift | Native iOS apps |
| Unity / C# | openfort-csharp-unity | Games (Windows, macOS, Android, iOS, WebGL) |
| Vanilla JS / TS | @openfort/openfort-js | Custom UIs, any JS framework, bare-metal access |
Which SDK should I use?
- If you're building a React or Next.js web app → use
@openfort/react(includes pre-built auth modal + wallet UI)- If you're building a React Native / Expo mobile app → use
@openfort/react-native- If you're building a native iOS app → use
OpenfortSwift- If you're building a Unity game → use
openfort-csharp-unity- If you're building with Svelte, Vue, Angular, vanilla JS, or need custom login flows → use
@openfort/openfort-js
For detailed API references per SDK, see the reference files:
references/openfort-react.md — React / Next.jsreferences/openfort-react-native.md — React Native / Exporeferences/openfort-swift.md — iOS / Swiftreferences/openfort-unity.md — Unity / C#references/openfort-js.md — Vanilla JS / TSThe fastest way to get started with a web app is the Openfort CLI:
npm create openfort@latest
# or
pnpm create openfort@latest
# or
yarn create openfort
This scaffolds a new project with all dependencies, configurations, and a working embedded wallet integration. You can select:
The CLI currently scaffolds React web apps. For other platforms, follow the manual setup below.
Before integrating any SDK, get your keys from the Openfort Dashboard:
pk_test_...) — identifies your projectyour-shield-publishable-key) — for embedded wallet encryptionpol_..., optional) — for gasless transactions@openfort/reactPre-built auth modal + wallet UI. Supports Ethereum (EVM) and Solana chains.
# Ethereum (EVM) — with wagmi
npm install @openfort/react @tanstack/react-query wagmi viem
# Solana — no wagmi needed
npm install @openfort/react @tanstack/react-query @solana/kit
VITE_OPENFORT_PUBLISHABLE_KEY=pk_test_...
VITE_SHIELD_PUBLISHABLE_KEY=your-shield-publishable-key
VITE_WALLET_CONNECT_PROJECT_ID=... # Optional — enables external wallets (EVM only)
VITE_FEE_SPONSORSHIP_ID=pol_... # Optional — gasless transactions
VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT=https://... # Backend endpoint for automatic recovery
For Next.js, use NEXT_PUBLIC_ prefix instead of VITE_.
The provider stack is: QueryClientProvider → WagmiProvider → OpenfortWagmiBridge → OpenfortProvider.
"use client" // Required in Next.js App Router
import { OpenfortProvider, AuthProvider } from '@openfort/react'
import { getDefaultConfig, OpenfortWagmiBridge } from '@openfort/react/wagmi'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { base, baseSepolia } from 'viem/chains'
import { createConfig, http, WagmiProvider } from 'wagmi'
const config = createConfig(
getDefaultConfig({
appName: 'My App',
chains: [base, baseSepolia],
walletConnectProjectId: import.meta.env.VITE_WALLET_CONNECT_PROJECT_ID,
transports: {
[base.id]: http('https://your-base-rpc.com'),
[baseSepolia.id]: http(),
},
}),
)
const queryClient = new QueryClient()
const walletConfig = {
shieldPublishableKey: import.meta.env.VITE_SHIELD_PUBLISHABLE_KEY!,
ethereum: {
ethereumFeeSponsorshipId: import.meta.env.VITE_FEE_SPONSORSHIP_ID,
},
createEncryptedSessionEndpoint: import.meta.env.VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT,
connectOnLogin: true,
}
export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<WagmiProvider config={config}>
<OpenfortWagmiBridge>
<OpenfortProvider
publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY!}
walletConfig={walletConfig}
uiConfig={{
theme: 'midnight',
mode: 'dark',
authProviders: [AuthProvider.GOOGLE, AuthProvider.EMAIL_OTP, AuthProvider.WALLET],
}}
>
{children}
</OpenfortProvider>
</OpenfortWagmiBridge>
</WagmiProvider>
</QueryClientProvider>
)
}
No wagmi, no bridge — just QueryClientProvider → OpenfortProvider.
"use client"
import { OpenfortProvider, AuthProvider, ChainTypeEnum } from '@openfort/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
const queryClient = new QueryClient()
const walletConfig = {
shieldPublishableKey: import.meta.env.VITE_SHIELD_PUBLISHABLE_KEY!,
chainType: ChainTypeEnum.SVM,
solana: { cluster: 'mainnet-beta' },
createEncryptedSessionEndpoint: import.meta.env.VITE_CREATE_ENCRYPTED_SESSION_ENDPOINT,
connectOnLogin: true,
}
export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
<OpenfortProvider
publishableKey={import.meta.env.VITE_OPENFORT_PUBLISHABLE_KEY!}
walletConfig={walletConfig}
uiConfig={{
theme: 'midnight',
mode: 'dark',
authProviders: [AuthProvider.GOOGLE, AuthProvider.EMAIL_OTP],
}}
>
{children}
</OpenfortProvider>
</QueryClientProvider>
)
}
OpenfortButton adapts to user state automatically:
import { OpenfortButton } from '@openfort/react'
function App() {
return <OpenfortButton />
}
// Auth
import { useUser, useEmailAuth, useEmailOtpAuth, usePhoneOtpAuth, useOAuth, useGuestAuth, useSignOut } from '@openfort/react'
// Wallet
import { useEthereumEmbeddedWallet } from '@openfort/react/ethereum'
import { useSolanaEmbeddedWallet } from '@openfort/react/solana'
// UI control
import { useUI } from '@openfort/react' // open/close modal programmatically
// wagmi integration
import { useWalletAuth, useChainIsSupported } from '@openfort/react/wagmi'
uiConfig={{
theme: 'midnight', // 'auto' | 'web95' | 'retro' | 'soft' | 'midnight' | 'minimal' | 'rounded' | 'nouns'
mode: 'dark', // 'light' | 'dark' | 'auto'
authProviders: [AuthProvider.GOOGLE, AuthProvider.EMAIL_OTP, AuthProvider.WALLET],
authProvidersLength: 4, // How many to show before "More options"
walletRecovery: {
allowedMethods: [RecoveryMethod.AUTOMATIC, RecoveryMethod.PASSKEY],
defaultMethod: RecoveryMethod.AUTOMATIC,
},
// termsOfServiceUrl, privacyPolicyUrl, disclaimer, customTheme, logo, etc.
}}
@openfort/react-nativeNative mobile SDK. Supports Ethereum and Solana embedded wallets.
npx --yes expo install expo-apple-authentication expo-application expo-crypto expo-secure-store expo-constants
npm install react-native-get-random-values @openfort/react-native
# Optional:
npm install react-native-passkeys # For passkey recovery
npx expo install expo-web-browser expo-linking # For OAuth
react-native-get-random-values must be imported first:
// entrypoint.ts
import "react-native-get-random-values";
import "expo-router/entry";
Update package.json: { "main": "entrypoint.ts" }
import { OpenfortProvider, RecoveryMethod } from "@openfort/react-native";
import Constants from "expo-constants";
export default function RootLayout() {
return (
<OpenfortProvider
publishableKey={Constants.expoConfig?.extra?.openfortPublishableKey}
walletConfig={{
shieldPublishableKey: Constants.expoConfig?.extra?.openfortShieldPublishableKey,
recoveryMethod: RecoveryMethod.AUTOMATIC,
feeSponsorshipId: Constants.expoConfig?.extra?.openfortPolicyId,
createEncryptedSessionEndpoint: Constants.expoConfig?.extra?.openfortShieldRecoveryEndpoint,
}}
supportedChains={[{
id: 84532,
name: 'Base Sepolia',
nativeCurrency: { name: 'Base Sepolia Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://sepolia.base.org'] } },
}]}
verbose={__DEV__}
>
{/* Your app */}
</OpenfortProvider>
);
}
No wagmi, no bridge, no QueryClientProvider, no manual WebView — just OpenfortProvider.
import { useEmbeddedEthereumWallet } from '@openfort/react-native' // EVM
import { useEmbeddedSolanaWallet } from '@openfort/react-native' // Solana
import { AuthBoundary } from '@openfort/react-native' // Auth state boundary
import { useUser, useEmailAuth, useEmailAuthOtp, useOAuth, useGuestAuth, useSignOut } from '@openfort/react-native'
<AuthBoundary
loading={<LoadingScreen />}
unauthenticated={<LoginScreen />}
error={(error) => <ErrorScreen error={error} />}
>
<MainApp />
</AuthBoundary>
OpenfortSwiftNative Swift SDK for iOS apps. Ethereum-only (EVM).
Add via Swift Package Manager: https://github.com/openfort-xyz/swift-sdk.git
Add OFConfig.plist to your Xcode project:
openfortPublishableKey (required)shieldPublishableKey (required)shieldEncryptionKey or shieldEncryptionEndpoint (for recovery)import OpenfortSwift
// In AppDelegate or App init:
try OFSDK.initialize()
// With third-party auth:
try OFSDK.initialize(thirdPartyAuth: .init(
provider: .firebase,
getAccessToken: { try await Auth.auth().currentUser?.getIDToken() ?? "" }
))
let sdk = OFSDK.shared
// Authenticate
try await sdk.logInWithEmailPassword(email: "[email protected]", password: "pass")
try await sdk.initOAuth(provider: .google, redirectTo: "myapp://callback")
// Configure embedded wallet (auto create or recover)
let account = try await sdk.configureEmbeddedWallet(
chainId: 80002,
recoveryMethod: .automatic(encryptionSession: session)
)
// Get EIP-1193 provider
let provider = try sdk.getEthereumProvider(policy: "pol_...")
// Send transaction
let txHash = try await provider.request(method: "eth_sendTransaction", params: [...])
openfort-csharp-unityGame SDK supporting Windows, macOS, Android, iOS, and WebGL.
Via UPM git URL: https://github.com/openfort-xyz/openfort-csharp-unity.git?path=/src/Packages/OpenfortSDK
Requires UniTask v2.3.3.
using Openfort;
var sdk = await OpenfortSDK.Init("pk_test_...", "your-shield-publishable-key");
// Authenticate
await sdk.LogInWithEmailPassword("[email protected]", "pass");
// Configure embedded wallet
var account = await sdk.ConfigureEmbeddedWallet(
chainId: 80002,
recoveryMethod: RecoveryMethod.Automatic,
encryptionSession: session
);
// Get provider and send transaction
var provider = sdk.GetEthereumProvider(policy: "pol_...");
var txHash = await provider.Request("eth_sendTransaction", new object[] { txParams });
@openfort/openfort-jsLow-level SDK for custom login flows and bare-metal API access. Works with any framework (Svelte, Vue, Angular, etc.) or no framework at all.
npm install @openfort/openfort-js@latest
import { Openfort } from '@openfort/openfort-js'
const openfort = new Openfort({
baseConfiguration: { publishableKey: 'pk_test_...' },
shieldConfiguration: { shieldPublishableKey: 'your-shield-publishable-key' },
})
await openfort.waitForInitialization()
// Authenticate
await openfort.auth.logInWithEmailPassword({ email: '[email protected]', password: 'pass' })
// Or OAuth
const url = await openfort.auth.initOAuth({ provider: OAuthProvider.GOOGLE, redirectTo: '...' })
// Configure embedded wallet (auto create or recover)
const account = await openfort.embeddedWallet.configure({
chainId: 80002,
recoveryParams: {
recoveryMethod: RecoveryMethod.AUTOMATIC,
encryptionSession: await fetchEncryptionSession(),
},
})
// Get EIP-1193 provider
const provider = await openfort.embeddedWallet.getEthereumProvider({ policy: 'pol_...' })
// Send transaction
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [{ from: account.address, to: '0x...', value: '0x...', data: '0x' }],
})
// Viem
import { createWalletClient, custom } from 'viem'
const walletClient = createWalletClient({ chain: sepolia, transport: custom(provider) })
// Ethers.js v6
import { ethers } from 'ethers'
const ethersProvider = new ethers.BrowserProvider(provider)
const signer = await ethersProvider.getSigner()
All SDKs support the same auth methods:
Embedded wallets use client-side encryption. Recovery is needed when users switch devices:
Set up gas sponsorship in the dashboard under Policies and Fee Sponsorship, then pass the pol_... ID to your SDK config.
Openfort embedded wallets support any EVM chain and Solana. Configure chains in your SDK setup with chain IDs and RPC URLs.
tools
Always use this skill when the user wants to explore Openfort documentation, browse SDK source code, or use Openfort MCP tools. This is the general-purpose Openfort platform skill — use it for documentation lookup, source code navigation, and CLI operations. For building with embedded wallets, use the openfort-embedded-wallet skill. For backend wallets, use the openfort-backend-wallets skill. Trigger on: "Openfort docs", "search Openfort", "Openfort source code", "openfort CLI", "MCP tools", "Openfort SDK source", or general Openfort platform questions.
tools
Openfort CLI skill — use for all openfort command-line operations including login, wallet management, transactions, policies, and configuration. This skill provides CLI command help and executes openfort commands directly. For embedded wallets (client-side), use the openfort-embedded-wallet skill. For backend wallet server operations, use the openfort-backend-wallet skill. Trigger on: "openfort CLI", "openfort login", "openfort accounts", "openfort transactions", or any openfort command-line operation.
development
Create and operate Openfort backend wallets (developer custody) for EVM and Solana from server-side code. Use this skill whenever: creating backend wallets, sending transactions from server, importing/exporting private keys with RSA encryption, signing data/messages/typed-data server-side, EIP-7702 delegation, Solana transfers (SOL/SPL/USDC), gasless transactions, fee sponsorship, policy engine rules, wallet secret auth, webhooks, or operating wallets programmatically without user interaction. Trigger on: "backend wallet", "developer custody", "server-side wallet", "walletSecret", "sendTransaction from backend", "import private key", "export private key", "EIP-7702", "Solana transfer server", "gasless", "fee sponsorship", "policy rules", "batch transactions", "sponsor gas", "webhook", or any server-side wallet operation with Openfort.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.