# Para > Non-custodial embedded wallet infrastructure using MPC for building embedded wallet integrations across web, mobile, and server platforms supporting EVM, Solana, and Cosmos chains. ## Para SDK Implementation Supplement > Concrete build-time details: packages, initialization, authentication, signing, connectors, account abstraction, and configuration for building Para integrations. ## 1. SDK Package Matrix All `@getpara/*` packages must use the **same version** as each other. Always pin every `@getpara/*` dependency to the same release to avoid mismatches. ### Core SDKs | Package | Use When | |---------|----------| | `@getpara/react-sdk` | React/Next.js apps with ParaProvider + hooks + ParaModal | | `@getpara/react-sdk-lite` | React apps using Para only through a connector (Wagmi/Graz) - lighter bundle, exports `ParaWeb` | | `@getpara/web-sdk` | Non-React web frameworks (Vue, Svelte) - exports `ParaWeb` imperative class | | `@getpara/server-sdk` | Node.js/Bun/Deno server-side - exports `Para` (aliased as `ParaServer`) | | `@getpara/react-native-wallet` | React Native / Expo mobile apps | ### Signing Integration Packages | Package | Web3 Library | Companion Deps | |---------|-------------|----------------| | `@getpara/viem-v2-integration` | Viem 2.x | `viem@^2.27.0` | | `@getpara/ethers-v6-integration` | Ethers 6.x | `ethers@^6.13.0` | | `@getpara/ethers-v5-integration` | Ethers 5.x | `ethers@^5.8.0` | | `@getpara/solana-web3.js-v1-integration` | Solana Web3.js 1.x | `@solana/web3.js@^1.98.0` | | `@getpara/solana-signers-v2-integration` | Solana Signers 2.x | `@solana/web3.js@^1.98.0` | | `@getpara/cosmjs-v0-integration` | CosmJS 0.34+ | `@cosmjs/stargate@^0.34.0`, `@cosmjs/proto-signing@^0.34.0` | ### Connector Packages | Package | Connector Ecosystem | Companion Deps | |---------|-------------------|----------------| | `@getpara/wagmi-v2-integration` | Wagmi 2.x / 3.x | `wagmi@^2.15.0`, `viem@^2.27.0`, `@tanstack/react-query@^5.0.0` | | `@getpara/rainbowkit-wallet` | RainbowKit | `@rainbow-me/rainbowkit@^2.0.0`, `wagmi`, `viem` | | `@getpara/graz-integration` | Graz (Cosmos) | `graz@^0.4.1` | ### Wallet Connector Packages (for bulk pregen) | Package | Purpose | |---------|---------| | `@getpara/evm-wallet-connectors` | EVM external wallet connections | | `@getpara/solana-wallet-connectors` | Solana external wallet connections | | `@getpara/cosmos-wallet-connectors` | Cosmos external wallet connections | ### Account Abstraction Companion Deps | AA Provider | Key Packages | |------------|-------------| | Alchemy (ERC-4337) | `@aa-sdk/core`, `@account-kit/infra`, `@account-kit/smart-contracts` | | Alchemy (EIP-7702) | Same as above, uses `createModularAccountV2Client` with `mode: "7702"` | | ZeroDev (ERC-4337) | `@zerodev/sdk`, `@zerodev/ecdsa-validator` | | ZeroDev (EIP-7702) | Same packages, uses `create7702KernelAccount` + `KERNEL_V3_3` | | Gelato | `@gelatonetwork/smartwallet` | | Thirdweb | `thirdweb` | | Rhinestone | `@rhinestone/sdk` | | Porto | `porto` | ### Required Companion Dependency All React examples require: ``` @tanstack/react-query@^5.0.0 ``` --- ## 2. Initialization Patterns ### React / Next.js (ParaProvider) ```tsx // src/components/ParaProvider.tsx "use client"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Environment, ParaProvider as ParaSDKProvider } from "@getpara/react-sdk"; const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? ""; const ENVIRONMENT = (process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA; if (!API_KEY) { throw new Error("API key is not defined. Please set NEXT_PUBLIC_PARA_API_KEY in your environment variables."); } const queryClient = new QueryClient(); export function ParaProvider({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` The `config` prop accepts: ```ts interface ParaProviderConfig { appName: string; // required disableAutoSessionKeepAlive?: boolean; // disable automatic session refresh disableEmbeddedModal?: boolean; rpcUrl?: string; farcasterMiniAppConfig?: FarcasterMiniAppConfig; } ``` Wrap your app root (e.g., `layout.tsx`): ```tsx import { ParaProvider } from "@/components/ParaProvider"; export default function RootLayout({ children }) { return {children}; } ``` `ParaProvider` also accepts an optional `callbacks` prop for event handling: ```ts type Callbacks = { onLogout?: (event) => void; onLogin?: (event) => void; onAccountSetup?: (event) => void; onAccountCreation?: (event) => void; onSignMessage?: (event) => void; onSignTransaction?: (event) => void; onWalletsChange?: (event) => void; onWalletCreated?: (event) => void; onPregenWalletClaimed?: (event) => void; }; ``` ### Vue / Svelte (ParaWeb - imperative) ```ts // src/lib/para.ts import { Environment, ParaWeb } from "@getpara/web-sdk"; const API_KEY = import.meta.env.VITE_PARA_API_KEY; const ENVIRONMENT = (import.meta.env.VITE_PARA_ENVIRONMENT as Environment) || Environment.BETA; if (!API_KEY) { throw new Error("API key is not defined. Please set VITE_PARA_API_KEY in your environment variables."); } export const para = new ParaWeb(ENVIRONMENT, API_KEY); ``` ### Server (ParaServer) ```ts import { Para as ParaServer, Environment } from "@getpara/server-sdk"; const PARA_API_KEY = process.env.PARA_API_KEY; const PARA_ENVIRONMENT = (process.env.PARA_ENVIRONMENT as Environment) || Environment.BETA; const para = new ParaServer(PARA_ENVIRONMENT, PARA_API_KEY); ``` ### React Native / Expo (ParaMobile) ```ts import ParaMobile, { Environment } from "@getpara/react-native-wallet"; const para = new ParaMobile( Environment.BETA, process.env.EXPO_PUBLIC_PARA_API_KEY, undefined, { disableWorkers: true } ); ``` The `{ disableWorkers: true }` option is **required** for React Native because Web Workers are not available. ### Chrome Extension (storage overrides) ```ts import { ParaWeb } from "@getpara/react-sdk"; import { chromeStorageOverrides } from "../chrome-storage"; export const para = new ParaWeb(ENVIRONMENT, API_KEY, { ...chromeStorageOverrides, useStorageOverrides: true, }); // Shared init promise - await in any script that needs Para export const paraReady = para.init(); ``` The `chromeStorageOverrides` object maps `chrome.storage.local` / `chrome.storage.session` to replace `localStorage` / `sessionStorage` which are not available in extension service workers. See `examples-hub/web/with-chrome-extension/src/lib/chrome-storage.ts` for the full implementation. --- ## 3. Environment Variables ### Naming Conventions by Framework | Framework | Prefix | Example | |-----------|--------|---------| | Next.js (client) | `NEXT_PUBLIC_` | `NEXT_PUBLIC_PARA_API_KEY` | | Next.js (server) | none | `PARA_API_KEY` | | Vite (Vue/Svelte) | `VITE_` | `VITE_PARA_API_KEY` | | Expo | `EXPO_PUBLIC_` | `EXPO_PUBLIC_PARA_API_KEY` | | Node.js server | none | `PARA_API_KEY` | ### Common Variables ```env # Required - Para PARA_API_KEY=your_api_key PARA_ENVIRONMENT=BETA # or PRODUCTION # Client-side variants (pick one based on framework) NEXT_PUBLIC_PARA_API_KEY=your_api_key NEXT_PUBLIC_PARA_ENVIRONMENT=BETA VITE_PARA_API_KEY=your_api_key VITE_PARA_ENVIRONMENT=BETA EXPO_PUBLIC_PARA_API_KEY=your_api_key # Server-side pregen encryption ENCRYPTION_KEY=a_32_byte_string_exactly_this_l # Must be exactly 32 bytes # Account Abstraction ALCHEMY_API_KEY=your_alchemy_key ALCHEMY_GAS_POLICY_ID=your_policy_id ALCHEMY_RPC_URL=https://arb-sepolia.g.alchemy.com/v2/your_key ZERODEV_PROJECT_ID=your_zerodev_project_id ZERODEV_BUNDLER_RPC=https://rpc.zerodev.app/api/v2/bundler/... ZERODEV_PAYMASTER_RPC=https://rpc.zerodev.app/api/v2/paymaster/... ZERODEV_ARBITRUM_SEPOLIA_RPC=https://rpc.zerodev.app/api/v2/rpc/... # REST API PARA_REST_BASE_URL=https://api.beta.getpara.com # or https://api.getpara.com ``` --- ## 4. Authentication Flows ### Email Auth (React hooks pattern) ```tsx import { useSignUpOrLogIn, useWaitForLogin, useWaitForWalletCreation, type AuthStateVerify, } from "@getpara/react-sdk"; // In your hook/component: const { signUpOrLogIn, isPending: isSigningUp } = useSignUpOrLogIn(); const { waitForLogin, isPending: isWaitingForLogin } = useWaitForLogin(); const { waitForWalletCreation, isPending: isWaitingForWallet } = useWaitForWalletCreation(); const shouldCancel = useRef(false); // Step 1: Submit email signUpOrLogIn( { auth: { email } }, { onSuccess: (authState) => { if (authState?.stage === "verify") { const verifyState = authState as AuthStateVerify; // verifyState.loginUrl -> open in iframe or popup for passkey verification // verifyState.nextStage -> "signup" (new user) or "login" (returning user) const isNewUser = verifyState.nextStage === "signup"; handleAuthComplete(isNewUser); } }, onError: (err) => { /* handle error */ }, } ); // Step 2: Wait for auth completion function handleAuthComplete(isNewUser: boolean) { if (isNewUser) { waitForWalletCreation( { isCanceled: () => shouldCancel.current }, { onSuccess: () => { /* user is logged in with wallet */ } } ); } else { waitForLogin( { isCanceled: () => shouldCancel.current }, { onSuccess: () => { /* user is logged in */ } } ); } } ``` The `loginUrl` from Step 1 must be displayed to the user in an iframe or popup - it's the passkey verification page hosted by Para. ### Email Auth (Imperative / Svelte / Vue) ```ts import { para } from "@/lib/para"; // Step 1: Submit email const authState = await para.signUpOrLogIn({ auth: { email } }); if (authState.stage === "verify" && authState.loginUrl) { const isNewUser = authState.nextStage === "signup"; // Show authState.loginUrl to user in iframe/popup // Step 2: Wait for completion if (isNewUser) { await para.waitForWalletCreation({ isCanceled: () => shouldCancel }); } else { const result = await para.waitForLogin({ isCanceled: () => shouldCancel }); if (result.needsWallet) { await para.waitForWalletCreation({ isCanceled: () => shouldCancel }); } } } ``` ### OAuth Auth (React hooks) ```tsx import { useVerifyOAuth, useVerifyFarcaster, useWaitForLogin, useWaitForWalletCreation, type TOAuthMethod, } from "@getpara/react-sdk"; const { verifyOAuth } = useVerifyOAuth(); const { verifyFarcaster } = useVerifyFarcaster(); // Standard OAuth (Google, Apple, Discord, Facebook, Twitter) verifyOAuth( { method: "GOOGLE", // TOAuthMethod excluding "TELEGRAM" | "FARCASTER" onOAuthUrl: (url) => { window.open(url, "oauth", "popup=true"); }, isCanceled: () => shouldCancel.current || !!popupWindow.current?.closed, }, { onSuccess: (authState) => { if (authState.stage === "done") { handleAuthComplete(authState.isNewUser); } }, } ); // Farcaster verifyFarcaster( { onConnectUri: (uri) => { window.open(uri, "farcaster", "popup=true"); }, isCanceled: () => shouldCancel.current || !!popupWindow.current?.closed, }, { onSuccess: (authState) => { if (authState.stage === "done") { handleAuthComplete(authState.isNewUser); } }, } ); ``` ### Verify iframe listener When showing the `loginUrl` in an iframe, listen for the close message: ```ts useEffect(() => { const portalBase = "https://app.beta.getpara.com"; // or https://app.getpara.com const handleMessage = (event: MessageEvent) => { if (!event.origin.startsWith(portalBase)) return; if (event.data?.type === "CLOSE_WINDOW" && event.data.success) { setVerifyUrl(null); } }; window.addEventListener("message", handleMessage); return () => window.removeEventListener("message", handleMessage); }, []); ``` --- ## 5. Signing Integration Code ### Viem v2 ```ts import { createParaAccount, createParaViemClient } from "@getpara/viem-v2-integration"; import { http, parseEther, parseGwei } from "viem"; import { sepolia } from "viem/chains"; // From client (using useClient hook): import { useClient } from "@getpara/react-sdk"; const client = useClient(); // returns the Para client instance // Create account + client const viemParaAccount = createParaAccount(para); // para = ParaServer or client const viemClient = createParaViemClient(para, { account: viemParaAccount, chain: sepolia, transport: http("https://ethereum-sepolia-rpc.publicnode.com"), }); // Sign transaction const request = await viemClient.prepareTransactionRequest({ account: viemParaAccount, to: "0x...", value: parseEther("0.0001"), gas: BigInt(21000), maxFeePerGas: parseGwei("20"), maxPriorityFeePerGas: parseGwei("3"), chain: sepolia, }); const signedTx = await viemClient.signTransaction(request); ``` ### Ethers v6 ```ts import { ParaEthersSigner } from "@getpara/ethers-v6-integration"; import { ethers } from "ethers"; const provider = new ethers.JsonRpcProvider("https://ethereum-sepolia-rpc.publicnode.com"); const signer = new ParaEthersSigner(para, provider as ethers.Provider); const address = await signer.getAddress(); const tx = { to: address, value: ethers.parseEther("0.0001"), nonce: await provider.getTransactionCount(address), gasLimit: 21000, gasPrice: (await provider.getFeeData()).gasPrice, }; await signer.signTransaction(tx); ``` ### Ethers v5 ```ts import { ParaEthersV5Signer } from "@getpara/ethers-v5-integration"; import { ethers } from "ethers"; const provider = new ethers.providers.JsonRpcProvider(RPC_URL); const signer = new ParaEthersV5Signer(client, provider); // client from useClient() await signer.signMessage("Hello, Para!"); await signer.sendTransaction(tx); ``` ### Solana Web3.js v1 ```ts import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration"; import { Connection, Transaction, SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; const connection = new Connection("https://api.testnet.solana.com"); const signer = new ParaSolanaWeb3Signer(para, connection); const { blockhash } = await connection.getLatestBlockhash(); const tx = new Transaction(); tx.recentBlockhash = blockhash; tx.feePayer = signer.sender; // PublicKey from Para wallet tx.add( SystemProgram.transfer({ fromPubkey: signer.sender, toPubkey: signer.sender, lamports: LAMPORTS_PER_SOL / 1000, }) ); await signer.signTransaction(tx); ``` ### CosmJS ```ts import { ParaProtoSigner } from "@getpara/cosmjs-v0-integration"; import { SigningStargateClient } from "@cosmjs/stargate"; const signer = new ParaProtoSigner(para, "cosmos"); // second arg is bech32 prefix const stargateClient = await SigningStargateClient.connectWithSigner( "https://rpc-rs.cosmos.nodestake.top/", signer ); const fee = { amount: [{ denom: "uatom", amount: "500" }], gas: "200000" }; await stargateClient.sign( signer.address, [{ typeUrl: "/cosmos.bank.v1beta1.MsgSend", value: { fromAddress: signer.address, toAddress: "cosmos1...", amount: [{ denom: "uatom", amount: "1000" }] } }], fee, "Signed with Para" ); ``` --- ## 6. Wallet Pre-generation (Server-Side) ### Create pre-generated wallet ```ts import { Para as ParaServer, Environment } from "@getpara/server-sdk"; import { encrypt } from "./encryption-utils"; import { setKeyShareInDB } from "./keySharesDB"; const para = new ParaServer(PARA_ENVIRONMENT, PARA_API_KEY); // Check if wallet already exists const walletExists = await para.hasPregenWallet({ pregenId: { email } }); // Create wallets for all chain types const wallets = await para.createPregenWalletPerType({ types: ["EVM", "SOLANA", "COSMOS"], pregenId: { email }, }); // Get and encrypt the user share const keyShare = para.getUserShare(); const encryptedKeyShare = await encrypt(keyShare); await setKeyShareInDB(email, encryptedKeyShare); ``` ### Restore user share for signing ```ts const keyShare = await getKeyShareInDB(email); const decryptedKeyShare = await decrypt(keyShare); await para.setUserShare(decryptedKeyShare); // Now para is ready for signing with any integration ``` ### AES-GCM Encryption Pattern ```ts // encryption-utils.ts const ALGORITHM = "AES-GCM"; const IV_LENGTH = 12; // ENCRYPTION_KEY env var must be exactly 32 bytes async function importSecretKey(keyString: string): Promise { const keyBuffer = Buffer.from(keyString, "utf-8"); return await crypto.subtle.importKey("raw", keyBuffer, { name: ALGORITHM }, false, ["encrypt", "decrypt"]); } export async function encrypt(text: string): Promise { const cryptoKey = await importSecretKey(process.env.ENCRYPTION_KEY); const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH)); const encoded = new TextEncoder().encode(text); const encrypted = await crypto.subtle.encrypt({ name: ALGORITHM, iv }, cryptoKey, encoded); return `${Buffer.from(iv).toString("base64")}:${Buffer.from(encrypted).toString("base64")}`; } export async function decrypt(encryptedText: string): Promise { const [ivBase64, dataBase64] = encryptedText.split(":"); const iv = new Uint8Array(Buffer.from(ivBase64, "base64")); const data = Buffer.from(dataBase64, "base64"); const cryptoKey = await importSecretKey(process.env.ENCRYPTION_KEY); const decrypted = await crypto.subtle.decrypt({ name: ALGORITHM, iv }, cryptoKey, data); return new TextDecoder().decode(decrypted); } ``` ### SQLite Storage Schema ```ts // keySharesDB.ts - uses `sqlite` + `sqlite3` packages import { open } from "sqlite"; import sqlite3 from "sqlite3"; const db = await open({ filename: "keyShares.db", driver: sqlite3.Database }); await db.exec(` CREATE TABLE IF NOT EXISTS keyShares ( email TEXT PRIMARY KEY NOT NULL, keyShare TEXT NOT NULL ) `); // Get const row = await db.get("SELECT keyShare FROM keyShares WHERE email = ?", [email]); // Upsert await db.run( "INSERT INTO keyShares (email, keyShare) VALUES (?, ?) ON CONFLICT(email) DO UPDATE SET keyShare = excluded.keyShare", [email, encryptedKeyShare] ); ``` --- ## 7. Account Abstraction Patterns ### Alchemy ERC-4337 ```ts import { alchemy, arbitrumSepolia } from "@account-kit/infra"; import { WalletClientSigner } from "@aa-sdk/core"; import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts"; import { createParaAccount, createParaViemClient } from "@getpara/viem-v2-integration"; const viemParaAccount = createParaAccount(para); const viemClient = createParaViemClient(para, { account: viemParaAccount, chain: arbitrumSepolia, transport: http(ALCHEMY_RPC_URL), }); const walletClientSigner = new WalletClientSigner(viemClient, "para"); const alchemyClient = await createModularAccountAlchemyClient({ transport: alchemy({ rpcUrl: ALCHEMY_RPC_URL }), chain: arbitrumSepolia, signer: walletClientSigner, policyId: ALCHEMY_GAS_POLICY_ID, }); const result = await alchemyClient.sendUserOperation({ uo: batchCalls }); await alchemyClient.waitForUserOperationTransaction(result); ``` ### Alchemy EIP-7702 ```ts import { createModularAccountV2Client } from "@account-kit/smart-contracts"; import { createParaAccount } from "@getpara/viem-v2-integration"; import type { SmartAccountSigner, LocalAccount } from "@aa-sdk/core"; const viemParaAccount = createParaAccount(para); // Wrap as SmartAccountSigner const paraSigner: SmartAccountSigner = { signerType: "para", inner: viemParaAccount, getAddress: async () => viemParaAccount.address, signMessage: async (message) => viemParaAccount.signMessage({ message }), signTypedData: async (typedData) => viemParaAccount.signTypedData(typedData), signAuthorization: async (auth) => viemParaAccount.signAuthorization(auth), }; const alchemyClient = await createModularAccountV2Client({ mode: "7702", transport: alchemy({ rpcUrl: ALCHEMY_RPC_URL }), chain: arbitrumSepolia, signer: paraSigner, policyId: ALCHEMY_GAS_POLICY_ID, }); ``` ### ZeroDev ERC-4337 ```ts import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"; import { createKernelAccount, createKernelAccountClient, createZeroDevPaymasterClient } from "@zerodev/sdk"; import { getEntryPoint, KERNEL_V3_1 } from "@zerodev/sdk/constants"; const viemParaAccount = createParaAccount(para); const viemClient = createParaViemClient(para, { account: viemParaAccount, chain: arbitrumSepolia, transport: http(ZERODEV_RPC_URL) }); const publicClient = createPublicClient({ chain: arbitrumSepolia, transport: http(ZERODEV_RPC_URL) }); const entryPoint = getEntryPoint("0.7"); const ecdsaValidator = await signerToEcdsaValidator(viemClient, { signer: viemParaAccount, entryPoint, kernelVersion: KERNEL_V3_1, }); const kernelAccount = await createKernelAccount(publicClient, { plugins: { sudo: ecdsaValidator }, entryPoint, kernelVersion: KERNEL_V3_1, }); const kernelClient = createKernelAccountClient({ account: kernelAccount, chain: arbitrumSepolia, bundlerTransport: http(ZERODEV_BUNDLER_RPC), paymaster: { getPaymasterData: (userOp) => createZeroDevPaymasterClient({ chain: arbitrumSepolia, transport: http(ZERODEV_PAYMASTER_RPC) }) .sponsorUserOperation({ userOperation: userOp }), }, }); const hash = await kernelClient.sendUserOperation({ callData: await kernelClient.account.encodeCalls(calls) }); await kernelClient.waitForUserOperationReceipt({ hash, timeout: 30000 }); ``` ### ZeroDev EIP-7702 ```ts import { create7702KernelAccount, create7702KernelAccountClient } from "@zerodev/ecdsa-validator"; import { KERNEL_V3_3 } from "@zerodev/sdk/constants"; const kernelAccount = await create7702KernelAccount(publicClient, { signer: viemParaAccount, entryPoint: getEntryPoint("0.7"), kernelVersion: KERNEL_V3_3, }); const kernelClient = create7702KernelAccountClient({ account: kernelAccount, chain: arbitrumSepolia, bundlerTransport: http(ZERODEV_BUNDLER_RPC), paymaster: createZeroDevPaymasterClient({ chain: arbitrumSepolia, transport: http(ZERODEV_PAYMASTER_RPC) }), client: publicClient, }); const hash = await kernelClient.sendUserOperation({ calls }); ``` ### Gelato ERC-4337 ```ts import { accounts, createGelatoSmartWalletClient } from "@gelatonetwork/smartwallet"; const viemParaAccount = createParaAccount(para); const viemClient = createParaViemClient(para, { account: viemParaAccount, chain: sepolia, transport: http() }); const account = await accounts.kernel({ eip7702: false, signer: viemClient }); const gelatoClient = createGelatoSmartWalletClient({ account, apiKey: GELATO_API_KEY }); const result = await gelatoClient.execute({ payment: { type: "sponsored" }, calls: [{ to: "0x...", value: 0n, data: "0x" }], }); await result.wait(); ``` ### Gelato EIP-7702 ```ts import { gelato } from "@gelatonetwork/smartwallet/accounts"; import { createGelatoSmartWalletClient } from "@gelatonetwork/smartwallet"; const account = await gelato({ signer: viemClient }); // Different subpath from 4337's accounts.kernel() const gelatoClient = createGelatoSmartWalletClient({ account, apiKey: GELATO_API_KEY }); // Same execute pattern as 4337 ``` ### Thirdweb ERC-4337 ```ts import { createThirdwebClient, sendTransaction, prepareTransaction } from "thirdweb"; import { viemAdapter, smartWallet } from "thirdweb/wallets"; import { sepolia } from "thirdweb/chains"; const thirdwebClient = createThirdwebClient({ clientId: THIRDWEB_CLIENT_ID }); const personalAccount = viemAdapter.walletClient.fromViem({ walletClient: viemClient }); const wallet = smartWallet({ chain: sepolia, sponsorGas: true }); const smartAccount = await wallet.connect({ client: thirdwebClient, personalAccount }); const tx = prepareTransaction({ to: "0x...", value: 0n, chain: sepolia, client: thirdwebClient }); await sendTransaction({ account: smartAccount, transaction: tx }); ``` ### Porto EIP-7702 Porto operates on Base Sepolia with a unique EOA-to-smart-account upgrade flow. See `examples-hub/web/with-react-nextjs/aa-porto-7702/` for the full implementation using `Account.from()`, `Key.createSecp256k1()`, and `RelayActions.prepareUpgradeAccount()`. ### Rhinestone ERC-4337 Rhinestone provides cross-chain account abstraction using `@rhinestone/sdk`. See `examples-hub/web/with-react-nextjs/aa-rhinestone-4337/` for cross-chain USDC transfers (Arbitrum->Base) with sponsored transactions. --- ## 8. Connector Integrations ### Wagmi Connector ```ts // src/config/wagmi.ts import { paraConnector } from "@getpara/wagmi-v2-integration"; import { ParaWeb } from "@getpara/react-sdk-lite"; // or @getpara/react-sdk import { createConfig, http, cookieStorage, createStorage } from "wagmi"; import { sepolia } from "wagmi/chains"; // Initialize ParaWeb (not ParaProvider - connector manages its own UI) const para = new ParaWeb(Environment.BETA, API_KEY); const connector = paraConnector({ para, appName: "My App", chains: [sepolia], queryClient, authLayout: ["AUTH:FULL", "EXTERNAL:FULL"], oAuthMethods: ["APPLE", "DISCORD", "FACEBOOK", "FARCASTER", "GOOGLE", "TWITTER"], disableEmailLogin: false, disablePhoneLogin: false, logo: "/logo.svg", onRampTestMode: true, recoverySecretStepEnabled: true, twoFactorAuthEnabled: false, theme: { foregroundColor: "#222222", backgroundColor: "#FFFFFF", accentColor: "#888888", mode: "light", borderRadius: "none", font: "Inter", }, }); export const wagmiConfig = createConfig({ chains: [sepolia], connectors: [connector, injected(), metaMask(), coinbaseWallet()], ssr: true, storage: createStorage({ storage: cookieStorage }), transports: { [sepolia.id]: http(RPC_URL) }, }); ``` Important: The Wagmi connector uses `@getpara/react-sdk-lite` (not the full `@getpara/react-sdk`) to avoid bundle bloat, since the connector provides its own modal. ### RainbowKit ```ts // src/client/wagmi.ts import { getParaWallet } from "@getpara/rainbowkit-wallet"; import { connectorsForWallets } from "@rainbow-me/rainbowkit"; import { createConfig, http } from "wagmi"; import { sepolia } from "wagmi/chains"; const paraWallet = getParaWallet({ para, // ParaWeb instance chains: [sepolia], appName: "My App", // Same modal config options as paraConnector: oAuthMethods, theme, etc. }); const connectors = connectorsForWallets( [{ groupName: "Para", wallets: [paraWallet] }], { appName: "My App", projectId: WALLETCONNECT_PROJECT_ID } ); export const wagmiConfig = createConfig({ chains: [sepolia], connectors, transports: { [sepolia.id]: http(RPC_URL) }, }); // Providers: WagmiProvider + QueryClientProvider + RainbowKitProvider // Then use wagmi hooks: useSignMessage(), useSendTransaction(), etc. ``` ### Graz (Cosmos) ```tsx // src/context/Provider.tsx import { ParaGrazConnector } from "@getpara/graz-integration"; import { ParaWeb, Environment } from "@getpara/react-sdk-lite"; import { GrazProvider, defineChainInfo, type ParaGrazConfig } from "graz"; const para = new ParaWeb(Environment.BETA, API_KEY); const cosmosTestnet = defineChainInfo({ chainId: "provider", chainName: "Cosmos ICS Provider Testnet", rpc: "https://rpc.provider-sentry-01.ics-testnet.polypore.xyz", rest: "https://rest.provider-sentry-01.ics-testnet.polypore.xyz", bip44: { coinType: 118 }, bech32Config: { bech32PrefixAccAddr: "cosmos", /* ... other prefixes */ }, stakeCurrency: { coinDenom: "ATOM", coinMinimalDenom: "uatom", coinDecimals: 6 }, currencies: [{ coinDenom: "ATOM", coinMinimalDenom: "uatom", coinDecimals: 6 }], feeCurrencies: [{ coinDenom: "ATOM", coinMinimalDenom: "uatom", coinDecimals: 6 }], }); const paraConfig: ParaGrazConfig = { paraWeb: para as ParaGrazConfig["paraWeb"], connectorClass: ParaGrazConnector, // Note: connectorClass, not connector instance modalProps: { appName: "My App" }, queryClient: queryClient, }; // Wrap your app: {children} ``` Note: `defineChainInfo` and `ParaGrazConfig` type are imported from `graz`, not from `@getpara/graz-integration`. --- ## 9. Session Management ### Check and keep alive ```ts // Client-side (React hooks) import { useAccount, useKeepSessionAlive } from "@getpara/react-sdk"; const { isConnected } = useAccount(); // Imperative const isActive = await para.isSessionActive(); if (isActive) { await para.keepSessionAlive(); } ``` ### Export / Import session (server transfer) ```ts // Client: export session string const sessionString = await para.exportSession(); // Send sessionString to server via API call // Server: import session const serverPara = new ParaServer(PARA_ENVIRONMENT, PARA_API_KEY); await serverPara.importSession(sessionString); // Server can now sign on behalf of the user const isActive = await serverPara.isSessionActive(); ``` ### Issue JWT ```ts // Client-side import { useIssueJwt } from "@getpara/react-sdk"; const { issueJwt } = useIssueJwt(); const { token, keyId } = await issueJwt(); // Send `token` to your server for verification // Imperative const { token, keyId } = await para.issueJWT(); ``` --- ## 10. ParaModal Configuration The `paraModalConfig` prop on `` (or the config object for `paraConnector`) accepts: ```ts interface ParaModalConfig { // Authentication layout authLayout?: ("AUTH:FULL" | "AUTH:CONDENSED" | "EXTERNAL:FULL" | "EXTERNAL:CONDENSED")[]; // Email / Phone login toggles disableEmailLogin?: boolean; disablePhoneLogin?: boolean; defaultAuthIdentifier?: string; // Pre-fill email/phone // OAuth providers oAuthMethods?: TOAuthMethod[]; // TOAuthMethod = "APPLE" | "DISCORD" | "FACEBOOK" | "FARCASTER" | "GOOGLE" | "TWITTER" | "TELEGRAM" // Branding logo?: string; // recommended 372x160px // Theme theme?: { foregroundColor?: string; backgroundColor?: string; accentColor?: string; darkForegroundColor?: string; darkBackgroundColor?: string; darkAccentColor?: string; overlayBackground?: string; mode?: "light" | "dark"; borderRadius?: "none" | "xs" | "sm" | "md" | "lg" | "xl" | "full"; font?: string; oAuthLogoVariant?: "dark" | "light" | "default"; }; // Features onRampTestMode?: boolean; recoverySecretStepEnabled?: boolean; twoFactorAuthEnabled?: boolean; hideWallets?: boolean; // Hide wallet terminology isGuestModeEnabled?: boolean; // Enable guest login bareModal?: boolean; // No overlay backdrop embeddedModal?: boolean; // Embedded styling className?: string; // Custom CSS class // Account linking supportedAccountLinks?: ("EMAIL" | "PHONE" | "GOOGLE" | "EXTERNAL_WALLET")[]; // Step override currentStepOverride?: ModalStepProp; // Steps: AUTH_MAIN, AUTH_MORE, AWAITING_OAUTH, VERIFICATIONS, // BIOMETRIC_CREATION, PASSWORD_CREATION, SECRET, AWAITING_WALLET_CREATION, // ACCOUNT_MAIN, ACCOUNT_PROFILE, CHAIN_SWITCH, // EX_WALLET_MORE, EX_WALLET_SELECTED, // ADD_FUNDS_BUY, ADD_FUNDS_RECEIVE, ADD_FUNDS_WITHDRAW, // SETUP_2FA, VERIFY_2FA // Callbacks onModalStepChange?: (value: any) => void; onClose?: () => void; // Advanced overrides loginTransitionOverride?: (para: ParaWeb) => Promise; createWalletOverride?: (para: ParaWeb) => Promise<{ recoverySecret?: string; walletIds: any }>; } ``` > **Note:** Password and PIN screens render in an iframe and use the Developer Portal theme settings, not `paraModalConfig.theme`. --- ## 11. Mobile Setup Requirements ### Required Native Modules (Expo) ```json { "@craftzdog/react-native-buffer": "^6.1.0", "@getpara/react-native-wallet": "", "@peculiar/webcrypto": "^1.5.0", "@react-native-async-storage/async-storage": "^2.2.0", "react-native-keychain": "^10.0.0", "react-native-modpow": "^1.1.0", "react-native-passkey": "^3.3.2", "react-native-quick-base64": "^2.2.0", "react-native-quick-crypto": "^0.7.14", "react-native-worklets": "^0.5.1", "readable-stream": "^4.5.2" } ``` ### Metro Config (polyfills) ```js // metro.config.js const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); config.resolver.extraNodeModules = { crypto: require.resolve('react-native-quick-crypto'), buffer: require.resolve('@craftzdog/react-native-buffer'), }; module.exports = config; ``` ### Babel Config ```js // babel.config.js module.exports = function (api) { api.cache(true); return { presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'], plugins: ['react-native-worklets/plugin'], }; }; ``` ### iOS Passkey Setup For passkey support on iOS, you must configure your app's Associated Domains capability with a `webcredentials:` entry pointing to your domain. The domain must serve an `apple-app-site-association` file. See `examples-hub/mobile/with-expo-one-click-login/ios/` for an example Xcode project configuration. ### Key Init Option Always pass `{ disableWorkers: true }` when initializing `ParaMobile` - Web Workers are not available in React Native. --- ## 12. Vite Polyfill Configuration Vue and Svelte projects using Vite need `vite-plugin-node-polyfills` because the Para SDK uses Node.js built-in modules (`buffer`, `crypto`, `stream`). ```ts // vite.config.ts import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; // or svelte() import { nodePolyfills } from "vite-plugin-node-polyfills"; export default defineConfig({ plugins: [vue(), nodePolyfills()], }); ``` Install: ```bash npm install -D vite-plugin-node-polyfills ``` This is required for `@getpara/web-sdk`. The `@getpara/react-sdk` with Next.js does **not** need this (Next.js handles polyfills via `npx setup-para` postinstall script). --- ## 13. REST API Direct Usage For server-to-server integrations without the SDK, use the REST API directly. ### Base URLs | Environment | URL | |------------|-----| | Beta | `https://api.beta.getpara.com` | | Production | `https://api.getpara.com` | ### Authentication Header ``` X-API-Key: your_api_key X-Request-Id: # optional, for request tracing Content-Type: application/json ``` ### Endpoints ``` POST /v1/wallets - Create wallet GET /v1/wallets/{walletId} - Get wallet details POST /v1/wallets/{walletId}/sign-raw - Sign raw bytes (0x-prefixed hex) ``` ### Create Wallet Request ```json { "type": "EVM", "userIdentifier": "alice@example.com", "userIdentifierType": "EMAIL", "scheme": "DKLS" } ``` - `type`: `"EVM"` | `"SOLANA"` | `"COSMOS"` - `userIdentifierType`: `"EMAIL"` | `"PHONE"` | `"CUSTOM_ID"` | `"GUEST_ID"` | `"TELEGRAM"` | `"DISCORD"` | `"TWITTER"` - `scheme`: `"DKLS"` | `"CGGMP"` | `"ED25519"` (optional, defaults based on wallet type) - `cosmosPrefix`: string (optional, for Cosmos wallets) ### Sign Raw Request ```json { "data": "0xdeadbeef..." } ``` ### Error Codes | Code | Meaning | |------|---------| | 201 | Created | | 200 | Success | | 400 | Bad request (invalid params) | | 401 | Unauthorized (invalid API key) | | 404 | Not found | | 409 | Conflict (wallet already exists) | | 429 | Rate limited | | 500 | Server error | ### Example Node.js client ```ts const PARA_API_KEY = process.env.PARA_API_KEY; const BASE_URL = process.env.PARA_REST_BASE_URL ?? "https://api.beta.getpara.com"; async function callPara(path: string, options: { method?: string; body?: unknown } = {}): Promise { const { method = "GET", body } = options; const headers: Record = { "X-API-Key": PARA_API_KEY, "X-Request-Id": crypto.randomUUID(), }; if (body) headers["Content-Type"] = "application/json"; const res = await fetch(`${BASE_URL}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, }); const data = await res.json(); if (!res.ok) throw new Error(`Para ${res.status}: ${JSON.stringify(data)}`); return data as T; } ``` --- ## 14. React Hook Reference ### CSS Import ParaModal requires its stylesheet: ```tsx import "@getpara/react-sdk/styles.css"; ``` Add this in your root layout or ParaProvider component. ### From `@getpara/react-sdk` **Account & Connection:** - `useAccount()` -> `{ isConnected, isConnecting, address, embedded }` - `embedded.wallets` contains wallet array - `useClient()` -> Para client instance (for passing to signer constructors) - `useIsFullyLoggedIn()` -> `boolean | undefined` - `useParaStatus()` -> `ParaStatus` (readiness and Farcaster Mini App detection) - `useModal()` -> `{ isOpen, openModal, closeModal }` - `useWallet()` -> `Wallet | null | undefined` (active wallet) - `useWalletState()` -> `{ selectedWallet, setSelectedWallet, updateSelectedWallet }` - `useLinkedAccounts()` -> `LinkedAccounts & { userId: string }` **Authentication:** - `useSignUpOrLogIn()` -> `{ signUpOrLogIn, signUpOrLogInAsync, isPending }` - `useVerifyOAuth()` -> `{ verifyOAuth, isPending }` - `useVerifyFarcaster()` -> `{ verifyFarcaster, isPending }` - `useVerifyNewAccount()` -> `{ verifyNewAccount, verifyNewAccountAsync }` - `useWaitForLogin()` -> `{ waitForLogin, isPending }` - `useWaitForWalletCreation()` -> `{ waitForWalletCreation, isPending }` - `useLoginExternalWallet()` -> `{ loginExternalWallet, loginExternalWalletAsync }` - `useLogout()` -> `{ logout, logoutAsync }` - `useAddAuthMethod()` -> `{ addCredential, addCredentialAsync }` - `useLinkAccount()` -> `{ linkAccount, isPending, error }` **Signing (low-level):** - `useSignMessage()` -> `{ signMessage, signMessageAsync }` - signs base64-encoded message with wallet ID - `useSignTransaction()` -> `{ signTransaction, signTransactionAsync }` **Session:** - `useKeepSessionAlive()` -> `{ keepSessionAlive, keepSessionAliveAsync }` - `useIssueJwt()` -> `{ issueJwt, issueJwtAsync }` -> `{ token, keyId }` **Wallet Management:** - `useCreateWallet()` -> `{ createWallet, createWalletAsync }` - `useCreateWalletPerType()` -> `{ createWalletPerType, createWalletPerTypeAsync }` - create wallets for multiple chain types - `useCreateGuestWallets()` -> `{ createGuestWallets, createGuestWalletsAsync }` - `useWalletBalance()` -> `string | null` - balance in native units ### From `@getpara/react-sdk/evm` EVM-specific hooks for direct Viem client access (requires `@getpara/viem-v2-integration` as a dependency): - `useViemClient({ address, walletClientConfig })` -> `{ viemClient }` - returns a Viem WalletClient pre-configured with the Para signer ```ts import { useAccount } from "@getpara/react-sdk"; import { useViemClient } from "@getpara/react-sdk/evm"; import { http } from "viem"; import { sepolia } from "viem/chains"; const { embedded } = useAccount(); const address = embedded?.wallets?.[0]?.address as `0x${string}`; const { viemClient } = useViemClient({ address, walletClientConfig: { chain: sepolia, transport: http() }, }); // viemClient.sendTransaction, viemClient.signMessage, etc. ``` This is the preferred way to use Viem in React - no need to import `createParaAccount` / `createParaViemClient` directly. ### From `@getpara/react-sdk/viem` - `useViemAccount({ address? })` -> `{ viemAccount: Account | null, isLoading }` - lower-level access to the viem Account without creating a full WalletClient ### From `@getpara/react-sdk/solana` - `useSolanaSigner({ rpc, walletId? })` -> `{ solanaSigner: SignerWalletAdapter | null, isLoading }` - for Solana Signers v2 integration ### From `@getpara/react-sdk/cosmos` - `useCosmjsProtoSigner({ prefix?, walletId?, messageSigningTimeoutMs? })` -> `{ protoSigner: OfflineDirectSigner | null, isLoading }` - for CosmJS proto signing - `useCosmjsAminoSigner({ prefix?, walletId?, messageSigningTimeoutMs? })` -> `{ aminoSigner: OfflineAminoSigner | null, isLoading }` - for CosmJS amino signing ```ts // CosmJS example using hooks import { useCosmjsProtoSigner } from "@getpara/react-sdk/cosmos"; import { SigningStargateClient } from "@cosmjs/stargate"; const { protoSigner } = useCosmjsProtoSigner({ prefix: "cosmos" }); const stargateClient = await SigningStargateClient.connectWithSigner(RPC_URL, protoSigner); ``` --- ## 15. Testing Credentials ### Beta Environment - **Environment**: `Environment.BETA` / `"BETA"` - **Portal base URL**: `https://app.beta.getpara.com` - **API base URL**: `https://api.beta.getpara.com` ### Test Email Pattern Use any email ending in `@test.getpara.com` (e.g., `dev@test.getpara.com`, `test1@test.getpara.com`). **Any OTP code works** (e.g., `123456`, `000000`). ### Test Phone Numbers Use US numbers (+1) with format `(area code)-555-xxxx` (e.g., `(425)-555-1234`, `(206)-555-9876`). **Any OTP code works**. ### User Limits 50 users per beta account. Delete test users via [Developer Portal](https://developer.getpara.com) -> Users section. Users can only be deleted in BETA; in production, wallets are permanent. ### Post-Install Setup Many Next.js examples include a `postinstall` script: ```json "postinstall": "npx setup-para" ``` This configures Next.js webpack to handle Para SDK's WASM and worker files. Always run it after `npm install`. --- ## 16. Examples Hub Directory Map ### Web - Framework Setup | Directory | Framework | SDK | Key Concept | |-----------|----------|-----|-------------| | `web/with-react-nextjs/para-modal/` | Next.js | `@getpara/react-sdk` | ParaProvider + ParaModal quickstart | | `web/with-react-vite/` | React + Vite | `@getpara/react-sdk` | Vite with React | | `web/with-svelte-vite/` | Svelte + Vite | `@getpara/web-sdk` | Imperative ParaWeb init | | `web/with-vue-vite/` | Vue + Vite | `@getpara/web-sdk` | Imperative ParaWeb init | | `web/with-react-tanstack-start/` | TanStack Start | `@getpara/react-sdk` | Full-stack meta-framework | | `web/with-chrome-extension/` | Chrome Ext | `@getpara/react-sdk` | Storage overrides | | `web/with-pwa/` | PWA | `@getpara/react-sdk` | Progressive Web App | ### Web - Authentication | Directory | Auth Method | |-----------|------------| | `web/with-react-nextjs/custom-email-auth/` | Email + passkey verification | | `web/with-react-nextjs/custom-phone-auth/` | Phone + OTP verification | | `web/with-react-nextjs/custom-oauth-auth/` | OAuth (Google, Apple, etc.) + Farcaster | | `web/with-react-nextjs/custom-combined-auth/` | All auth methods combined | ### Web - Signing Libraries | Directory | Library | Package | |-----------|---------|---------| | `web/with-react-nextjs/signer-viem-v2/` | Viem 2.x | `@getpara/viem-v2-integration` | | `web/with-react-nextjs/signer-ethers-v5/` | Ethers 5.x | `@getpara/ethers-v5-integration` | | `web/with-react-nextjs/signer-ethers-v6/` | Ethers 6.x | `@getpara/ethers-v6-integration` | | `web/with-react-nextjs/signer-solana-web3/` | Solana Web3.js | `@getpara/solana-web3.js-v1-integration` | | `web/with-react-nextjs/signer-solana-anchor/` | Anchor | `@getpara/solana-web3.js-v1-integration` | | `web/with-react-nextjs/signer-solana-signers-v2/` | Solana Signers v2 | `@getpara/solana-signers-v2-integration` | | `web/with-react-nextjs/signer-cosmjs/` | CosmJS | `@getpara/react-sdk` (uses `useCosmjsProtoSigner` from `/cosmos`) | ### Web - Connectors | Directory | Connector | Package | |-----------|-----------|---------| | `web/with-react-nextjs/connector-wagmi/` | Wagmi v2 | `@getpara/wagmi-v2-integration` + `@getpara/react-sdk-lite` | | `web/with-react-nextjs/connector-rainbowkit/` | RainbowKit | `@getpara/rainbowkit-wallet` | | `web/with-react-nextjs/connector-graz/` | Graz (Cosmos) | `@getpara/graz-integration` | | `web/with-react-nextjs/connector-reown-appkit/` | Reown AppKit | `@getpara/wagmi-v2-integration` | ### Web - Account Abstraction | Directory | Provider | Standard | |-----------|----------|----------| | `web/with-react-nextjs/aa-alchemy-4337/` | Alchemy | ERC-4337 | | `web/with-react-nextjs/aa-alchemy-7702/` | Alchemy | EIP-7702 | | `web/with-react-nextjs/aa-zerodev-4337/` | ZeroDev | ERC-4337 | | `web/with-react-nextjs/aa-zerodev-7702/` | ZeroDev | EIP-7702 | | `web/with-react-nextjs/aa-gelato-4337/` | Gelato | ERC-4337 | | `web/with-react-nextjs/aa-gelato-7702/` | Gelato | EIP-7702 | | `web/with-react-nextjs/aa-thirdweb-4337/` | Thirdweb | ERC-4337 | | `web/with-react-nextjs/aa-rhinestone-4337/` | Rhinestone | ERC-4337 | | `web/with-react-nextjs/aa-porto-7702/` | Porto | EIP-7702 | ### Web - Modal Variants | Directory | Chain Focus | |-----------|------------| | `web/with-react-nextjs/para-modal/` | Default (all chains) | | `web/with-react-nextjs/para-modal-evm/` | EVM only | | `web/with-react-nextjs/para-modal-solana/` | Solana only | | `web/with-react-nextjs/para-modal-cosmos/` | Cosmos only | | `web/with-react-nextjs/para-modal-multichain/` | Explicit multi-chain | | `web/with-react-nextjs/para-pregen-claim/` | Pre-generated wallet claiming | ### Server | Directory | Runtime | Key Features | |-----------|---------|-------------| | `server/with-node/` | Node.js | Pregen wallets, Viem/Ethers/Solana/CosmJS signing, Alchemy AA, ZeroDev AA | | `server/with-bun/` | Bun | Session-based signing, Alchemy/ZeroDev EIP-7702 | | `server/with-deno/` | Deno | Session-based signing, Alchemy/ZeroDev EIP-7702 | | `server/rest-with-node/` | Node.js | REST API direct usage (no SDK, raw fetch) | ### Mobile | Directory | Platform | Key Features | |-----------|----------|-------------| | `mobile/with-react-native/` | React Native | Standard RN setup | | `mobile/with-expo-one-click-login/` | Expo | One-click login, passkeys, native crypto polyfills | ### Advanced Patterns | Directory | Pattern | |-----------|---------| | `advanced-patterns/client-auth-server-sign/` | Client authenticates, exports session, server signs | | `advanced-patterns/with-bulk-pregen/` | Bulk wallet pre-generation with multi-chain claiming UI | ### DeFi Integrations | Directory | Integration | |-----------|------------| | `defi-integrations/with-jupiter-dex-api/` | Jupiter DEX (Solana) | | `defi-integrations/with-squid-router-api/` | Squid cross-chain router | | `defi-integrations/with-relay-bridge-api/` | Relay Protocol bridge | --- --- # Command Reference Source: https://docs.getpara.com/v2/cli/commands import { Link } from "/snippets/v2/components/ui/link.mdx"; ## Authentication ### `para login` Authenticate with your Para developer account. Opens your browser for a secure OAuth flow with PKCE verification. ```bash para login ``` | Flag | Description | |---|---| | `--no-browser` | Print a URL instead of opening the browser (for headless/CI environments) | After login, the CLI automatically selects your first organization and project if none are configured. Sessions are stored at `~/.config/para/credentials.json` with `0600` file permissions. The session is validated server-side on each CLI invocation. Sessions are shared across key environments — logging in once gives you access to both beta and prod keys. ### `para logout` Clear stored authentication credentials. ```bash para logout ``` | Flag | Description | |---|---| | `--all` | Clear all stored sessions | The CLI sends a best-effort server-side session invalidation. ### `para auth status` Check whether your current session is valid. This performs a server-side validation, not just a local check. ```bash para auth status ``` ```bash Output Status: Authenticated Email: dev@example.com Expires: 3/10/2026, 12:00:00 PM ``` With `--json`: ```json { "authenticated": true, "email": "dev@example.com", "userId": "usr_abc123", "expiresAt": 1741608000000 } ``` ### `para whoami` Show the current authenticated user and active context — organization, project, and environment. ```bash para whoami ``` ```bash Output Email: dev@example.com User ID: usr_abc123 Key Environment: beta Organization: My Company (admin) Project: proj_def456 Session Expires: 3/10/2026, 12:00:00 PM ``` Organization shows the name and your role. Project shows the raw project ID. If the organization ID can't be resolved to a name, the raw ID is shown instead. --- ## Configuration Configuration resolves from multiple sources: CLI flags, environment variables (`PARA_ENVIRONMENT`, `PARA_ORG_ID`, `PARA_PROJECT_ID`), `.pararc`, global config, then defaults. See [Installation](/v2/cli/installation#configuration) for the full resolution chain. ### `para config get` Read configuration values. Without a key, shows all values from both global and project config with their source. ```bash para config get ``` ```bash Output defaultEnvironment (global): beta environment (.pararc): beta organizationId (.pararc): org_xyz789 projectId (.pararc): proj_def456 ``` Read a specific key: ```bash para config get defaultEnvironment ``` Project config (`.pararc`) takes precedence over global config (`~/.config/para/config.json`). ### `para config set` Set a configuration value. ```bash para config set ``` | Flag | Description | |---|---| | `--local` | Write to `.pararc` in the current directory instead of global config | #### Valid Keys | Key | Values | |---|---| | `defaultEnvironment` | `beta`, `prod` | | `defaultOrganizationId` | Any valid organization ID | | `defaultProjectId` | Any valid project ID | #### Examples ```bash # Set global default key environment para config set defaultEnvironment prod # Set global default org para config set defaultOrganizationId org_xyz789 ``` `para config set --local` writes the key name as-is (e.g., `defaultEnvironment`) into `.pararc`, but `para init` uses different key names (`environment`, `organizationId`, `projectId`). Use `para init` to create project-level config — it ensures the correct key names are used. ### `para config unset` Remove a configuration value. ```bash para config unset ``` | Flag | Description | |---|---| | `--local` | Remove from `.pararc` instead of global config | ```bash para config unset defaultOrganizationId para config unset environment --local ``` ### `para init` Create a `.pararc` configuration file in the current directory. This pins the organization, project, and environment for anyone working in this directory. ```bash para init ``` | Flag | Description | |---|---| | `--force` | Overwrite an existing `.pararc` file | In interactive mode, you'll be prompted to select a key environment (`beta` or `prod`). In non-interactive mode (`--no-input`), the current key environment from global config is used. The resulting `.pararc` file: ```json { "environment": "beta", "organizationId": "org_xyz789", "projectId": "proj_def456" } ``` The `.pararc` writer rejects keys that contain sensitive terms (`session`, `token`, `secret`, `credential`, `password`, `apikey`, `api_key`) to prevent accidental credential storage in version-controlled files. --- ## Organizations ### `para orgs list` List all organizations you belong to. ```bash para orgs list ``` ```bash Output Name ID Plan My Company org_xyz789 growth (active) Side Project org_abc123 free ``` The `(active)` indicator shows which organization is currently selected. With `--json`, returns an array of organization objects. ### `para orgs switch` Set the active organization. This updates your global config so subsequent commands use this org. ```bash para orgs switch [org-id] ``` Without an `org-id`, an interactive selector is shown: ```bash para orgs switch ``` ```bash Output ◆ Select an organization │ ○ My Company (org_xyz789) (current) │ ● Side Project (org_abc123) └ ``` With a specific ID: ```bash para orgs switch org_abc123 ``` After switching organizations, your previous project ID remains in the config but may point to a project in the old org. Run `para projects switch` to select a project in the new organization. --- ## Projects All project commands require an active organization. Set one with `para orgs switch` if you haven't already. ### `para projects list` List projects in your active organization. ```bash para projects list ``` | Flag | Description | |---|---| | `--include-archived` | Include archived projects in the list | ```bash Output Name ID Framework my-app proj_def456 nextjs (active) backend proj_ghi789 vite ``` ### `para projects switch` Set the active project. ```bash para projects switch [project-id] ``` Without a `project-id`, an interactive selector shows all active projects in the current organization. ### `para projects create` Create a new project in the active organization. ```bash para projects create ``` | Flag | Description | |---|---| | `-n, --name ` | Project name | | `-d, --description ` | Project description | | `--framework ` | Framework (`nextjs`, `vite`, `react-native`, etc.) | Without flags, you'll be prompted interactively for a name. ```bash para projects create -n "my-new-app" --framework nextjs ``` ### `para projects update` Update a project's name, description, or framework. ```bash para projects update [project-id] ``` | Flag | Description | |---|---| | `-n, --name ` | New project name | | `-d, --description ` | New description | | `--framework ` | Framework (`REACT`, `NEXT`, `VITE`, etc.) | | `--package-manager ` | Package manager (`NPM`, `YARN`, `PNPM`) | Uses the active project if no `project-id` is given. Without flags, prompts interactively. ### `para projects archive` Archive a project. Its API keys stop working immediately. This is reversible with `restore`. ```bash para projects archive [project-id] ``` Uses the active project if no `project-id` is given. | Flag | Description | |---|---| | `-y, --yes` | Skip confirmation prompt | Archiving a project immediately disables all of its API keys. Active users will lose access. ### `para projects restore` Restore a previously archived project. ```bash para projects restore ``` Use `para projects list --include-archived` to find the ID of archived projects. --- ## API Keys All key commands require an active organization and project. Set them with `para orgs switch` and `para projects switch`. ### `para keys list` List API keys for the active project. ```bash para keys list ``` | Flag | Description | |---|---| | `--include-archived` | Include archived keys | ```bash Output Name ID API Key Env Status prod-key key_abc123 para_beta_a1b2... BETA active test-key key_def456 para_beta_c3d4... BETA archived ``` ### `para keys get` Get details of an API key. Without a key ID, the CLI auto-resolves the key from your active project and key environment. ```bash para keys get [key-id] ``` | Flag | Description | |---|---| | `--show-secret` | Show the full secret key (unmasked) | | `--copy` | Copy the public API key to clipboard | | `--copy-secret` | Copy the secret key to clipboard | ```bash para keys get # Get the beta key (default) para keys get -e prod # Get the prod key para keys get abc-123 --copy # Copy a specific key ``` ### `para keys create` Create a new API key in the active project. ```bash para keys create ``` | Flag | Description | |---|---| | `-n, --name ` | Internal key name | | `--display-name ` | Display name shown to users | The secret key is only shown once at creation time. Save it immediately. ### `para keys rotate` Rotate an API key. The old key stops working immediately. Without a key ID, the CLI auto-resolves the key from your active project and key environment. ```bash para keys rotate [key-id] ``` | Flag | Description | |---|---| | `--secret` | Rotate the secret key instead of the public key | | `-y, --yes` | Skip confirmation prompt | ```bash para keys rotate # Rotate the beta key para keys rotate -e prod # Rotate the prod key para keys rotate --secret # Rotate the secret key ``` Key rotation is irreversible. The old key stops working immediately after rotation. ### `para keys archive` Archive (revoke) an API key. The key stops working immediately. Without a key ID, the CLI auto-resolves the key from your active project and key environment. ```bash para keys archive [key-id] ``` | Flag | Description | |---|---| | `-y, --yes` | Skip confirmation prompt | ```bash para keys archive # Archive the beta key para keys archive -e prod # Archive the prod key ``` ### `para keys config` Configure settings for an API key. Without a sub-category, opens an interactive menu to browse all categories. ```bash para keys config [key-id] ``` The CLI auto-resolves the key from your active project and key environment (`-e beta` or `-e prod`). If multiple keys exist for the same environment, you'll be prompted to choose one. #### Show Configuration View the current configuration for an API key without entering an edit flow. Shows all settings organized by category. ```bash para keys config show [category] [key-id] ``` | Argument | Description | |---|---| | `[category]` | Filter by category: `security`, `branding`, `setup`, `ramps`, `webhooks` | | `[key-id]` | API key ID (resolved from project + environment if omitted) | ```bash para keys config show # Show all configuration para keys config show security # Show security settings only para keys config show ramps # Show ramp settings only para keys config show --json # Full config as JSON (for scripting) para keys config show security --json # Single category as JSON ``` ```bash Example output Para (beta) Security Auth Methods PASSKEY, PASSWORD Origins https://myapp.com Session Length 1440 minutes (1 day) Tx Popups enabled IP Allowlist (none — all IPs allowed) Branding Foreground #333333 Background #ffffff Accent (default) Font Helvetica ... ``` Use `para keys config show --json` to pipe configuration into other tools or audit scripts. The JSON output uses raw values (arrays, booleans, `null`) rather than display strings. #### Security Configure auth methods, origins, session length, and IP restrictions. ```bash para keys config security [key-id] ``` | Flag | Description | |---|---| | `--origins ` | Comma-separated allowed origins (empty string to clear) | | `--auth-methods ` | Auth methods: `PASSKEY`, `PASSWORD`, `PIN` (comma-separated). `PASSWORD` and `PIN` cannot be enabled simultaneously | | `--session-length ` | Session length in minutes (5–43200) | | `--transaction-popups` | Enable transaction popups | | `--no-transaction-popups` | Disable transaction popups | | `--ip-allowlist ` | Comma-separated CIDR blocks (empty string to clear) | ```bash para keys config security --origins "https://myapp.com,https://staging.myapp.com" --auth-methods "PASSKEY,PASSWORD" ``` #### Branding Configure colors, fonts, social links, and email settings. ```bash para keys config branding [key-id] ``` | Flag | Description | |---|---| | `--foreground-color ` | Foreground color (`#RGB` or `#RRGGBB`) | | `--fg-color ` | Foreground color (alias) | | `--background-color ` | Background color | | `--bg-color ` | Background color (alias) | | `--accent-color ` | Accent color | | `--font ` | Font: `Arial`, `Courier New`, `Georgia`, `Helvetica`, `Lucida Sans`, `Tahoma`, `Times New Roman`, `Trebuchet MS` | | `--homepage-url ` | Homepage URL (HTTPS) | | `--twitter-url ` | Twitter/X profile URL | | `--linkedin-url ` | LinkedIn company URL | | `--github-url ` | GitHub URL | | `--verify-url ` | Verification email URL (HTTPS) | | `--email-welcome` / `--no-email-welcome` | Toggle welcome email | | `--email-backup-kit` / `--no-email-backup-kit` | Toggle backup kit email | #### Setup / Networks Configure wallet types and native passkey settings. ```bash para keys config setup [key-id] ``` | Flag | Description | |---|---| | `--wallet-types ` | Wallet types — prefix with `~` for optional: `"EVM,~SOLANA,~COSMOS,~STELLAR"` | | `--cosmos-prefix ` | Cosmos address prefix | | `--team-id ` | Apple Team ID (10 chars) | | `--bundle-id ` | Apple bundle identifier | | `--android-package ` | Android package name | | `--android-fingerprints ` | Comma-separated SHA256 fingerprints | #### On/Off Ramps Configure buy, receive, and withdraw settings. ```bash para keys config ramps [key-id] ``` | Flag | Description | |---|---| | `--buy-enabled` / `--no-buy-enabled` | Toggle buy | | `--receive-enabled` / `--no-receive-enabled` | Toggle receive | | `--withdraw-enabled` / `--no-withdraw-enabled` | Toggle withdraw | | `--providers ` | Comma-separated ordered providers: `RAMP`, `STRIPE`, `MOONPAY` | | `--ramp-api-key ` | Ramp API key | | `--default-buy-amount ` | Default buy amount (0.0001–999999) | | `--default-on-ramp-asset ` | Default on-ramp asset | | `--default-on-ramp-network ` | Default on-ramp network | #### Webhooks Configure webhook endpoints, events, and signing secrets. ```bash para keys config webhooks [key-id] ``` | Flag | Description | |---|---| | `--url ` | Webhook endpoint URL (HTTPS) | | `--events ` | Comma-separated events: `user.created`, `wallet.created`, `transaction.signed`, `send.broadcasted`, `send.confirmed`, `send.failed`, `wallet.pregen_claimed`, `user.external_wallet_verified` | | `--enabled` / `--no-enabled` | Toggle the webhook | | `--status` | Show current webhook configuration | | `--test` | Send a test webhook | | `--rotate-secret` | Rotate the webhook signing secret | | `--delete` | Remove webhook configuration | | `-y, --yes` | Skip confirmation for destructive operations | ```bash para keys config webhooks --url "https://api.myapp.com/webhooks" --events "user.created,wallet.created" --enabled ``` See for event type details and signature verification. --- ## Scaffold a Project ### `para create` Scaffold a new application with Para SDK pre-configured. The interactive wizard walks you through template, network, auth, and wallet selection. ```bash para create [app-name] ``` | Flag | Description | |---|---| | `-t, --template