React SDK
Selected Privy-compatible public API declarations from @shard-lab/nc-waas-react (the Pouchi Wallet React SDK). Entry points: . (React), ./server, ./wagmi, ./smart-wallets, ./solana, ./core (no React).
Hooks at a glance
Section titled “Hooks at a glance”| Name | What it does | Status |
|---|---|---|
PrivyProvider, NcWaasProvider |
Provider that wraps the app. apiUrl, appId (or tenantId), config.defaultChain (or chain) |
Supported |
usePrivy |
ready, authenticated, user, login, logout, getAccessToken, signMessage, exportWallet (explanation only), account linking |
Supported |
useLogin, useLogout |
Open and complete the login modal, logout callbacks | Supported |
useLoginWithEmail |
Email code login. New users complete wallet protection on the Pouchi screen (sendCode, loginWithCode) |
Supported |
useLoginWithOAuth |
google, apple, kakao, naver login. The tenant must turn it on and register the redirect URI | Needs tenant setup |
useLinkAccount |
Link email, social, passkey. Other methods reject through onError |
Supported |
useLoginWithPasskey |
Log in with a registered passkey | Supported |
useSignupWithPasskey |
Sign up with a passkey only | Not supported (rejects) |
useWallets |
One embedded smart wallet and its EIP-1193 provider | Supported |
useSendTransaction |
Transfers, ERC-20, contract calls. { hash } after the approval screen |
Supported |
useSignMessage, useSignTypedData |
ERC-1271 signatures (including EIP-712) | Supported |
useSendCalls, useSmartWallets |
Batches (EIP-5792, up to 8 calls) and the Privy smart wallet client | Supported |
useSessionApproval |
In-app session key delegation. Needs config.approvals.session.clientId |
Supported |
useWalletRecovery, useRecoverWallet |
Move the wallet to a new device, see a recovery in progress | Supported |
useRecoveryBackups |
List and delete backup methods | Supported |
useSessions, useRevokeSession, LoggedInDevices |
Logged-in devices and logging them out | Supported |
AssetMigrationCard, useAssetMigration |
Move assets from the old Privy wallet to the new wallet | Supported |
useCreateWallet |
Returns the wallet created at first login. Additional wallets reject | Partly supported |
useSessionSigners |
Privy session signers | Not supported (rejects). useSessionApproval or server delegation |
useSignTransaction |
Raw transaction signing | Not supported (rejects). useSendTransaction |
useFundWallet, useImportWallet, useCrossAppAccounts, useMfaEnrollment, useMfa, useSolanaWallets |
Privy-only features | Not supported (rejects or empty) |
useNcWaas, useNcWaasState |
Controller and state for building your own screens | Advanced |
Provider
Section titled “Provider”PrivyProvider
Section titled “PrivyProvider”function · react/provider.d.ts
Privy-compatible name. Migration also requires the Pouchi API URL, app ID and chain configuration.
const PrivyProvider: typeof NcWaasProvider;NcWaasProvider
Section titled “NcWaasProvider”function · react/provider.d.ts
function NcWaasProvider({ apiUrl, tenantId, appId, chain, config, core: suppliedCore, children }: NcWaasProviderProps): import("react").JSX.Element;NcWaasProviderProps
Section titled “NcWaasProviderProps”interface · react/provider.d.ts
interface NcWaasProviderProps { /** Pouchi API origin, for example `https://api.example.com`. */ apiUrl: string; /** Pouchi tenant id. */ tenantId?: string; /** Privy-compatible alias of `tenantId`, so `<PrivyProvider appId=...>` keeps working. */ appId?: string; /** Accepted for Privy compatibility; not used. */ clientId?: string; /** The tenant's chain. Falls back to `config.defaultChain`. */ chain?: Chain; config?: NcWaasConfig; /** Advanced: supply a preconfigured controller (custom storage, tests). */ core?: NcWaasCore; children?: ReactNode;}NcWaasConfig
Section titled “NcWaasConfig”interface · types.d.ts
interface NcWaasConfig { loginMethods?: LoginMethod[]; appearance?: NcWaasAppearance; /** Privy-compatible; external (Solana) wallet connectors are accepted and ignored. */ externalWallets?: Record<string, unknown>; /** Privy-compatible Solana settings (`solana.rpcs`); Pouchi wallets are EVM only, so this is accepted and ignored. */ solana?: Record<string, unknown>; /** Privy-compatible. Pouchi always creates the wallet at first login; the value is accepted and ignored. */ embeddedWallets?: { ethereum?: { createOnLogin?: "users-without-wallets" | "all-users" | "off"; }; solana?: { createOnLogin?: "users-without-wallets" | "all-users" | "off"; }; createOnLogin?: "users-without-wallets" | "all-users" | "off"; showWalletUIs?: boolean; }; /** Privy-compatible chain config. The chain must equal the tenant's Pouchi chain. */ defaultChain?: Chain; supportedChains?: Chain[]; approvals?: { /** * Session approvals: approve once, then one-tap confirmations inside a capped, short-lived * policy. Requires a client registered by the tenant for this app. See useSessionApproval. */ session?: SessionApprovalConfig; }; /** Recovery backup methods offered at wallet setup and in recovery (spec §4.6). */ backups?: { /** Google Drive app-data backup. Needs a Google OAuth web client ID for this app's origin (drive.appdata scope). */ googleDrive?: { clientId: string; apiBase?: string; uploadBase?: string; }; }; mfa?: Record<string, unknown>; walletConnectCloudProjectId?: string; fundingMethodConfig?: Record<string, unknown>; intl?: Record<string, unknown>; legal?: { termsAndConditionsUrl?: string; privacyPolicyUrl?: string; }; customAuth?: Record<string, unknown>; passkeys?: Record<string, unknown>; oauth?: { /** * The app page social login returns to. Default: the current page (origin + path). It must be registered * with Pouchi: an exact HTTPS URI in the tenant's OAuth redirect URIs (loopback pages on a registered * application origin also work outside production). */ redirectUrl?: string; }; transactions?: { /** * Optional tenant backend hook for contract calls. Without it, the signed-in user proposes * calls directly, limited by the tenant's userCallPolicy (registered contracts by default). */ propose?: TransactionProposer; /** How long `sendTransaction` waits for an on-chain hash. Default 120 s. */ timeoutMs?: number; };}NcWaasAppearance
Section titled “NcWaasAppearance”interface · types.d.ts
interface NcWaasAppearance { /** * "light", "dark", or "auto" (follows the OS, the default). Privy also accepts a hex background; it is treated as "auto". * Both palettes come from the same warm neutrals as the Pouchi web wallet. */ theme?: "light" | "dark" | "auto" | `#${string}`; /** * Primary button, focus ring and selection color. Defaults to the near-black ink of the Pouchi look (no saturated * accent). The button text switches to black or white for contrast automatically. */ accentColor?: `#${string}`; /** * Brand shown at the top left of every modal: an image URL or any React node (an inline SVG, a component). Without * it the header shows `appName` (default: the tenant name). Pass `appName` too so the image has an accessible name. */ logo?: string | ReactNode; /** Title of the first login screen. Default: "이메일로 로그인" / "Log in with email". */ landingHeader?: string; /** Subtitle of the first login screen. Default: "처음이면 지갑을 함께 만들어요. 비밀번호는 필요 없어요." */ loginMessage?: string; /** Application name used in copy, as the logo's accessible name, and in local passkey prompts. */ appName?: string; /** * Modal language: "ko" or "en", or the app's own language tag ("ko-KR" is Korean; any other language is English, * the sheets' only other language). Defaults to the browser language. Pass the app's current language here: when it * changes, the provider switches every sheet, including one that is open. */ locale?: "ko" | "en" | (string & {}); /** Hide the small platform attribution ("Powered by ...") under the login form. Default false. */ hideAttribution?: boolean; /** Privy-compatible; Pouchi wallets are Ethereum only, so this is accepted and ignored. */ walletChainType?: "ethereum-only" | "solana-only" | "ethereum-and-solana";}Login and user
Section titled “Login and user”usePrivy
Section titled “usePrivy”function · react/hooks.d.ts
function usePrivy(): UsePrivyResult;UsePrivyResult
Section titled “UsePrivyResult”type · react/hooks.d.ts
type UsePrivyResult = UsePrivyCore & ReturnType<typeof useUnsupportedUnlinks> & ReturnType<typeof useUnsupportedLinks>;useLogin
Section titled “useLogin”function · react/hooks.d.ts
function useLogin(callbacks?: { onComplete?: (params: LoginCompleteParams) => void; onError?: (error: NcWaasError) => void;}): { login: () => void;};LoginCompleteParams
Section titled “LoginCompleteParams”interface · react/hooks.d.ts
interface LoginCompleteParams { user: User; isNewUser: boolean; wasAlreadyAuthenticated: boolean; loginMethod: LoginMethodName | null; loginAccount: LinkedAccount | null;}useLogout
Section titled “useLogout”function · react/hooks.d.ts
function useLogout(callbacks?: { onSuccess?: () => void;}): { logout: () => Promise<void>;};useLoginWithEmail
Section titled “useLoginWithEmail”function · react/hooks.d.ts
Headless email login (Privy useLoginWithEmail). Drives the same flow as the modal; new
users still complete wallet protection and backup in the modal.
function useLoginWithEmail(callbacks?: { onComplete?: (params: LoginCompleteParams) => void; onError?: (error: NcWaasError) => void;}): { sendCode: ({ email }: { email: string; }) => Promise<void>; loginWithCode: ({ code }: { code: string; }) => Promise<void>; state: { status: string; error: Error | null; };};interface · types.d.ts
Privy-shaped user. id is stable per tenant wallet.
interface User { id: string; createdAt: Date; email?: { address: string; }; /** Privy-compatible; Pouchi has no phone login. */ phone?: { number: string; }; wallet?: UserWallet; google?: UserOAuthAccount; apple?: UserOAuthAccount; kakao?: UserOAuthAccount; naver?: UserOAuthAccount; linkedAccounts: LinkedAccount[]; mfaMethods: []; hasAcceptedTerms: boolean; isGuest: false; /** Pouchi specific. */ ncWaas: { tenantId: string; walletId: string; status: AccountResponse["status"]; deploymentStatus: AccountResponse["contract"]["deploymentStatus"]; };}UserWallet
Section titled “UserWallet”interface · types.d.ts
interface UserWallet { address: Hex; /** Always "ethereum"; "solana" is in the type only so Privy code that branches on it compiles. */ chainType: "ethereum" | "solana"; chainId: string; /** "privy" is kept for drop-in compatibility with code that looks for the embedded wallet. */ walletClientType: "privy" | (string & {}); connectorType: "embedded" | (string & {}); imported: boolean; delegated: boolean; walletIndex: number | null; recoveryMethod: "user-passcode" | (string & {}); /** Pouchi specific. */ walletId: string; /** Privy server-wallet id. Pouchi has no server wallets, so it is never set; use `walletId`. */ id?: string | null;}Social login, passkeys, linking
Section titled “Social login, passkeys, linking”useLoginWithOAuth
Section titled “useLoginWithOAuth”function · react/hooks.d.ts
Privy useLoginWithOAuth. initOAuth({ provider }) leaves for the provider’s sign-in and comes back to this
page (or config.oauth.redirectUrl); the provider finishes the login on load and calls onComplete.
Providers: those the tenant enabled among google, apple, kakao, naver.
function useLoginWithOAuth(callbacks?: { onComplete?: (params: LoginCompleteParams) => void; onError?: (error: NcWaasError) => void;}): { state: OAuthFlowState; initOAuth: (input: { provider: OAuthProvider | PrivyOnlyOAuthProvider; disableSignup?: boolean; }) => Promise<void>; loading: boolean;};useLinkAccount
Section titled “useLinkAccount”function · react/hooks.d.ts
Privy useLinkAccount: link methods plus success and error callbacks (OAuth links finish after the redirect).
function useLinkAccount(callbacks?: { onSuccess?: (params: LinkAccountSuccess) => void; onError?: (error: NcWaasError, details: { linkMethod: LoginMethodName; }) => void;}): { linkEmail: () => void; linkGoogle: () => void; linkApple: () => void; linkKakao: () => void; linkNaver: () => void; linkPhone: (..._args: unknown[]) => void; linkWallet: (..._args: unknown[]) => void; linkTwitter: (..._args: unknown[]) => void; linkDiscord: (..._args: unknown[]) => void; linkGithub: (..._args: unknown[]) => void; linkLinkedIn: (..._args: unknown[]) => void; linkTiktok: (..._args: unknown[]) => void; linkLine: (..._args: unknown[]) => void; linkSpotify: (..._args: unknown[]) => void; linkInstagram: (..._args: unknown[]) => void; linkFarcaster: (..._args: unknown[]) => void; linkTelegram: (..._args: unknown[]) => void; linkPasskey: () => void;};LinkAccountSuccess
Section titled “LinkAccountSuccess”interface · react/hooks.d.ts
interface LinkAccountSuccess { user: User; linkMethod: LoginMethodName; linkedAccount: LinkedAccount | null;}useLoginWithPasskey
Section titled “useLoginWithPasskey”function · react/hooks.d.ts
Privy useLoginWithPasskey: sign in with a passkey registered for this app (server-verified WebAuthn, discoverable
credential, no username). Register one with useLinkAccount().linkPasskey() while signed in.
function useLoginWithPasskey(callbacks?: { onComplete?: (params: LoginCompleteParams) => void; onError?: (error: NcWaasError) => void;}): { loginWithPasskey: () => Promise<void>; state: { status: "initial" | "generating-challenge" | "done" | "error"; error?: NcWaasError; };};useSignupWithPasskey
Section titled “useSignupWithPasskey”function · react/hooks.d.ts
Privy useSignupWithPasskey. A Pouchi wallet needs an account identity (email or a social login) for recovery
notices, so passkey-only signup is refused; sign up with email or a social account and
add a passkey with linkPasskey().
function useSignupWithPasskey(callbacks?: { onError?: (error: NcWaasError) => void;}): { signupWithPasskey: () => Promise<never>; state: { status: "initial"; };};Wallets and transactions
Section titled “Wallets and transactions”useWallets
Section titled “useWallets”function · react/hooks.d.ts
function useWallets(): { ready: boolean; wallets: ConnectedWallet[];};ConnectedWallet
Section titled “ConnectedWallet”interface · react/hooks.d.ts
interface ConnectedWallet { address: Hex; /** Privy v3: the chain family. Pouchi wallets are always "ethereum" (EVM smart wallets); "solana" is in the type only so Privy code that branches on it compiles. */ type: "ethereum" | "solana"; walletIndex: 0; /** CAIP-2, for example `eip155:450815`. */ chainId: string; walletClientType: "privy"; connectorType: "embedded"; imported: false; linked: true; meta: { name: string; id: string; icon?: string; }; getEthereumProvider(): Promise<NcWaasEthereumProvider>; /** Privy alias of getEthereumProvider. Raw-hash signing (secp256k1_sign) is refused with 4200. */ getProvider(): Promise<NcWaasEthereumProvider>; switchChain(chainId: number | `0x${string}`): Promise<void>; isConnected(): Promise<boolean>; disconnect(): void; /** Privy `wallet.sign(message)`: an ERC-1271 owner signature (WaaSWallet 4.1.0+). */ sign(message: string): Promise<Hex>;}getEthereumProviderFor
Section titled “getEthereumProviderFor”function · react/hooks.d.ts
One EIP-1193 provider per controller, shared by every wallet object.
function getEthereumProviderFor(core: NcWaasCore): NcWaasEthereumProvider;useSendTransaction
Section titled “useSendTransaction”function · react/hooks.d.ts
function useSendTransaction(callbacks?: { onSuccess?: (result: { hash: Hex; }) => void; onError?: (error: NcWaasError) => void;}): { sendTransaction: (input: UnsignedTransactionRequest, options?: SendTransactionOptions) => Promise<{ hash: Hex; } & Omit<SendTransactionResult, "hash">>;};SendTransactionOptions
Section titled “SendTransactionOptions”interface · react/hooks.d.ts
interface SendTransactionOptions { /** Privy-compatible. Sponsorship follows the tenant policy; `false` is not honored yet. */ sponsor?: boolean; uiOptions?: { header?: string; description?: string; buttonText?: string; showWalletUIs?: boolean; }; address?: string;}useSignMessage
Section titled “useSignMessage”function · react/hooks.d.ts
Privy useSignMessage: an ERC-1271 signature the wallet contract verifies
(isValidSignature, WaaSWallet 4.1.0+). The approval modal shows Pouchi’s decoding of the
message (SIWE, plain text, or opaque data) and its risk flags before PIN or passkey.
function useSignMessage(callbacks?: { onSuccess?: (result: { signature: Hex; }) => void; onError?: (error: NcWaasError) => void;}): { signMessage: (input: { message: string; }, _options?: unknown) => Promise<{ signature: Hex; }>;};useSignTypedData
Section titled “useSignTypedData”function · react/hooks.d.ts
Privy useSignTypedData (EIP-712): permits, Permit2, marketplace orders. High-risk requests need an explicit confirmation.
function useSignTypedData(callbacks?: { onSuccess?: (result: { signature: Hex; }) => void; onError?: (error: NcWaasError) => void;}): { signTypedData: (typedData: WaasTypedDataInput | string, _options?: unknown) => Promise<{ signature: Hex; }>;};useSendCalls
Section titled “useSendCalls”function · react/hooks.d.ts
Atomic batches (EIP-5792 wallet_sendCalls): one approval, executed together by the wallet contract; if one call
fails, none happen. Up to 8 calls. Returns the bundle id (for getCallsStatus) and the transaction hash.
function useSendCalls(callbacks?: { onSuccess?: (result: { id: string; hash: Hex; }) => void; onError?: (error: NcWaasError) => void;}): { sendCalls: (input: { calls: UnsignedTransactionRequest[]; }) => Promise<SendTransactionResult & { id: string; }>; getCallsStatus: (id: string) => Promise<import("../types").CallsStatus>;};useSmartWallets
Section titled “useSmartWallets”function · react/hooks.d.ts
Privy useSmartWallets() (@privy-io/react-auth/smart-wallets). The Pouchi wallet is itself an ERC-4337 smart
wallet, so client.sendTransaction({ calls }) is an atomic batch and a single { to, value, data } is a normal
transaction. Resolves to the transaction hash, as in Privy.
function useSmartWallets(): { client: { account: { address: `0x${string}`; }; chain: import("viem").Chain; sendTransaction: (input: (UnsignedTransactionRequest | { calls: UnsignedTransactionRequest[]; }) & { chain?: unknown; account?: unknown; }, _options?: unknown) => Promise<Hex>; signMessage: (input: { message: string; }) => Promise<`0x${string}`>; signTypedData: (typedData: WaasTypedDataInput | string) => Promise<`0x${string}`>; } | undefined; getClientForChain: ({ id }: { id: number; }) => Promise<{ account: { address: `0x${string}`; }; chain: import("viem").Chain; sendTransaction: (input: (UnsignedTransactionRequest | { calls: UnsignedTransactionRequest[]; }) & { chain?: unknown; account?: unknown; }, _options?: unknown) => Promise<Hex>; signMessage: (input: { message: string; }) => Promise<`0x${string}`>; signTypedData: (typedData: WaasTypedDataInput | string) => Promise<`0x${string}`>; } | undefined>;};TransactionProposer
Section titled “TransactionProposer”type · types.d.ts
type TransactionProposer = (input: TransactionProposalInput) => Promise<{ signingRequestId: string;}>;TransactionProposalInput
Section titled “TransactionProposalInput”interface · types.d.ts
Input passed to an optional tenant-backend transaction proposer. General contract calls use the configured proposer; without it, the signed-in user may propose calls within the app’s user-call policy. Native and recognized supported-token transfers bypass this hook. See the server integration guide for the bounded example.
interface TransactionProposalInput { tenantId: string; walletId: string; walletAddress: Hex; chainId: number; to: Hex; value: string; data: Hex; /** App access token (ES256 JWT) for the backend to verify with `verifyAuthToken`. Never the Pouchi session. */ accessToken: string;}UnsignedTransactionRequest
Section titled “UnsignedTransactionRequest”interface · types.d.ts
interface UnsignedTransactionRequest { /** A 0x address. Privy types this as a plain string, so a string is accepted and checked at runtime. */ to?: Hex | (string & {}); from?: Hex; value?: bigint | number | string | Hex; data?: Hex; chainId?: number | Hex; gas?: bigint | number | string | Hex; gasLimit?: bigint | number | string | Hex; maxFeePerGas?: bigint | number | string | Hex; maxPriorityFeePerGas?: bigint | number | string | Hex; nonce?: number | Hex;}SendTransactionResult
Section titled “SendTransactionResult”interface · types.d.ts
interface SendTransactionResult { hash: Hex; /** Pouchi specific: the ERC-4337 user operation hash and the API operation id. */ userOperationHash?: Hex; operationId?: string; sponsored: boolean;}Session approval
Section titled “Session approval”useSessionApproval
Section titled “useSessionApproval”function · react/hooks.d.ts
Approve once with PIN or passkey, then confirm transactions inside the session with one tap.
Requires config.approvals.session. The limits live in an owner-signed WaaSWallet policy,
so the contract enforces them whatever this UI does.
function useSessionApproval(): { session: import("../session-approvals").SessionApprovalState | null; active: boolean; startSession: (input: StartSessionInput) => Promise<import("../session-approvals").SessionApprovalState>; endSession: () => void;};StartSessionInput
Section titled “StartSessionInput”interface · session-approvals.d.ts
interface StartSessionInput { durationSeconds?: number; /** Total native value the session may move, decimal wei. "0" for calls without value. */ maxTotalValueWei: string; /** Per-call native value cap, decimal wei. Defaults to the total. */ maxValuePerCallWei?: string; maxCalls: number; contracts?: SessionPermissionInput[]; /** Addresses that may receive plain native transfers. */ nativeRecipients?: Hex[];}SessionApprovalConfig
Section titled “SessionApprovalConfig”interface · session-approvals.d.ts
interface SessionApprovalConfig { /** Client registered by the tenant (`/v1/tenant/client-authorization-clients`, web channel). */ clientId: string; clientName?: string; /** Must equal one of the client's registered webApprovalUris. Defaults to `${origin}/`. */ approvalUri?: string; defaultDurationSeconds?: number;}SessionApprovalState
Section titled “SessionApprovalState”interface · session-approvals.d.ts
interface SessionApprovalState { policyId: string; expiresAt: number; maxValuePerCallWei: string; valueAllowanceWei: string; spentWei: string; callsRemaining: number; permissions: SessionPermission[];}Recovery and devices
Section titled “Recovery and devices”useWalletRecovery
Section titled “useWalletRecovery”function · react/hooks.d.ts
Moving the wallet to this device and watching recoveries.
open() shows the sheet: approve from the old device, or use the account recovery flow with a passkey or recovery code.
function useWalletRecovery(): { /** This device can sign; when false, `open()` moves the wallet here. */ canSign: boolean; /** A recovery of this wallet that is waiting or running, on any device. */ pending: import("../types").WalletRecoveryState | null; open: () => void; cancel: () => Promise<void>;};useRecoverWallet
Section titled “useRecoverWallet”function · react/hooks.d.ts
Alias in Privy-style naming.
const useRecoverWallet: typeof useWalletRecovery;useRecoveryBackups
Section titled “useRecoveryBackups”function · react/hooks.d.ts
Account recovery uses a passkey or recovery code, configured once in Pouchi Home. The API snapshot below retains legacy backup fields. Passkey entries carry only ciphertext. App code never accesses key material; key-share operations stay inside the Pouchi vault frame.
function useRecoveryBackups(): { list: () => Promise<import("../core").RecoveryBackupSummary[]>; remove: (backupId: string) => Promise<void>; passkeySupported: () => Promise<boolean>; googleDriveAvailable: boolean;};useSessions
Section titled “useSessions”function · react/devices.d.ts
The wallet’s signed-in devices (spec §4.7). Each device keeps its own session; signing still needs that device’s key
share and PIN or passkey. revoke(deviceId) signs another device out (it must sign in again).
function useSessions(options?: { auto?: boolean;}): { devices: SignedInDevice[]; maxDevices: number | null; loading: boolean; error: string | undefined; refresh: () => Promise<SignedInDevice[]>; revoke: (deviceId: string | null) => Promise<void>;};useRevokeSession
Section titled “useRevokeSession”function · react/devices.d.ts
Signs one other device out (Privy-style naming).
function useRevokeSession(): { revokeSession: (deviceId: string | null) => Promise<void>;};LoggedInDevices
Section titled “LoggedInDevices”function · react/devices.d.ts
A small “signed-in devices” list for a settings screen, in the modal’s look.
function LoggedInDevices({ theme }: { theme?: "light" | "dark" | "auto";}): import("react").JSX.Element | null;Privy asset migration
Section titled “Privy asset migration”AssetMigrationCard
Section titled “AssetMigrationCard”function · react/migration-card.d.ts
Minimal, unstyled-by-default card using the modal tokens; apps can build their own with the hook.
function AssetMigrationCard({ oldProvider, tokens }: { oldProvider: Eip1193Like | undefined; tokens?: MigrationToken[];}): import("react").JSX.Element | null;useAssetMigration
Section titled “useAssetMigration”function · react/migration-card.d.ts
Plans and runs the move from an old wallet (for example the Privy embedded wallet’s
getEthereumProvider()) to the signed-in Pouchi wallet. The old wallet signs each step.
function useAssetMigration(options?: { tokens?: MigrationToken[];}): { destination: `0x${string}` | undefined; plan: MigrationPlan | null; results: MigrationStepResult[]; status: "error" | "done" | "ready" | "idle" | "planning" | "migrating"; error: string | undefined; prepare: (oldProvider: Eip1193Like) => Promise<MigrationPlan>; migrate: (oldProvider: Eip1193Like) => Promise<MigrationStepResult[]>;};Privy compatibility names (some reject)
Section titled “Privy compatibility names (some reject)”Selected compatibility names for Privy migration. Check the support table and behavior differences first. Where the model differs, calls reject with NOT_SUPPORTED (EIP-1193 4200).
useCreateWallet
Section titled “useCreateWallet”function · react/privy-compat.d.ts
Privy useCreateWallet: the wallet already exists after first login; one wallet for the user in the current app context.
function useCreateWallet(callbacks?: { onSuccess?: (result: { wallet: UserWallet; }) => void; onError?: (error: NcWaasError) => void;}): { createWallet: (options?: { createAdditional?: boolean; walletIndex?: number; }) => Promise<UserWallet>;};useSolanaWallets
Section titled “useSolanaWallets”function · react/privy-compat.d.ts
Privy useSolanaWallets: Pouchi wallets are Ethereum (EVM) only.
function useSolanaWallets(): { ready: boolean; wallets: Array<{ address: string; walletClientType: string; connectorType?: string; imported?: boolean; }>; createWallet: (..._args: unknown[]) => Promise<never>; exportWallet: (..._args: unknown[]) => Promise<never>;};useSignTransaction
Section titled “useSignTransaction”function · react/privy-compat.d.ts
Privy useSignTransaction: an ERC-4337 smart wallet has no raw signed transaction to return. Use
useSendTransaction (signs and submits a user operation) instead.
function useSignTransaction(callbacks?: { onError?: (error: NcWaasError) => void;}): { signTransaction: (_input: UnsignedTransactionRequest, _options?: unknown) => Promise<string>;};useFundWallet
Section titled “useFundWallet”function · react/privy-compat.d.ts
Privy useFundWallet: on-ramp and bridging are not part of Pouchi; fund the wallet address directly.
function useFundWallet(callbacks?: { onUserExited?: (params: unknown) => void;}): { fundWallet: FundWallet;};useCrossAppAccounts
Section titled “useCrossAppAccounts”function · react/privy-compat.d.ts
Privy useCrossAppAccounts.
function useCrossAppAccounts(): { linkCrossAppAccount: (_input: { appId: string; }) => Promise<never>; loginWithCrossAppAccount: (_input: { appId: string; }) => Promise<never>; sendTransaction: (..._args: unknown[]) => Promise<never>; signMessage: (..._args: unknown[]) => Promise<never>; signTypedData: (..._args: unknown[]) => Promise<never>;};useSessionSigners
Section titled “useSessionSigners”function · react/privy-compat.d.ts
Privy useSessionSigners (server-side delegated signing). Pouchi delegates through owner-signed, contract-
enforced policies instead: useSessionApproval for one-tap sessions, or agent permissions from the tenant backend.
function useSessionSigners(): { addSessionSigners: (_input: { address: string; signers: Array<{ signerId: string; policyIds?: string[]; }>; }) => Promise<never>; removeSessionSigners: (_input: { address: string; }) => Promise<never>;};useImportWallet
Section titled “useImportWallet”function · react/privy-compat.d.ts
Privy useImportWallet: Pouchi never takes a raw private key; its key is generated 2-of-3 and never assembled.
function useImportWallet(): { importWallet: (_input: { privateKey: string; }) => Promise<never>;};useMfaEnrollment
Section titled “useMfaEnrollment”function · react/privy-compat.d.ts
Privy useMfaEnrollment: Pouchi approval uses user authentication (such as a passkey or PIN).
function useMfaEnrollment(): { initEnrollmentWithSms: (..._args: unknown[]) => Promise<never>; submitEnrollmentWithSms: (..._args: unknown[]) => Promise<never>; initEnrollmentWithTotp: (..._args: unknown[]) => Promise<never>; submitEnrollmentWithTotp: (..._args: unknown[]) => Promise<never>; initEnrollmentWithPasskey: (..._args: unknown[]) => Promise<never>; submitEnrollmentWithPasskey: (..._args: unknown[]) => Promise<never>; showMfaEnrollmentModal: () => never; closeMfaEnrollmentModal: () => undefined;};useMfa
Section titled “useMfa”function · react/privy-compat.d.ts
Privy useMfa (no separate MFA step in Pouchi).
function useMfa(): { mfaMethods: string[]; init: (..._args: unknown[]) => Promise<never>; submit: (..._args: unknown[]) => Promise<never>; cancel: () => undefined;};Advanced: controller
Section titled “Advanced: controller”useNcWaas
Section titled “useNcWaas”function · react/provider.d.ts
The controller, for custom UIs (headless login and approval).
function useNcWaas(): NcWaasCore;useNcWaasState
Section titled “useNcWaasState”function · react/provider.d.ts
The raw controller state. Prefer the Privy-compatible hooks for app code.
function useNcWaasState(): NcWaasState;Errors
Section titled “Errors”NcWaasError
Section titled “NcWaasError”class · errors.d.ts
class NcWaasError extends Error { readonly code: NcWaasErrorCode; /** EIP-1193 numeric code. Wallet libraries read this as `error.code` via `toRpcError`. */ readonly rpcCode: number; /** Underlying API error code when the error came from the Pouchi API. */ readonly apiCode?: string; readonly status?: number; readonly data?: unknown; constructor(code: NcWaasErrorCode, message: string, options?: { apiCode?: string; status?: number; data?: unknown; cause?: unknown; });}NcWaasErrorCode
Section titled “NcWaasErrorCode”type · errors.d.ts
Errors surfaced by @shard-lab/nc-waas-react.
Every error carries a stable string code (for app logic) and, where it maps to
EIP-1193, a numeric rpcCode so wallet libraries such as viem and wagmi classify
it correctly (4001 user rejected, 4100 unauthorized, 4200 unsupported method,
4900 disconnected, 4901 chain disconnected).
type NcWaasErrorCode = "NOT_READY" | "NOT_AUTHENTICATED" | "USER_REJECTED" | "NOT_SUPPORTED" | "UNSUPPORTED_METHOD" | "UNSUPPORTED_CHAIN" | "INVALID_PARAMS" | "WALLET_NOT_READY" | "LOCAL_KEY_SHARE_MISSING" | "TRANSACTION_PROPOSER_REQUIRED" | "TRANSACTION_TIMEOUT" | "LOGIN_FLOW_INVALID" | "TOKEN_INVALID" | "UNSUPPORTED_CAPABILITY" | "API_ERROR";@shard-lab/nc-waas-react/server
Section titled “@shard-lab/nc-waas-react/server”Server only. Keep it out of browser bundles.
createNcWaasServerClient
Section titled “createNcWaasServerClient”function · server.d.ts
Privy new PrivyClient(...) counterpart.
function createNcWaasServerClient(options: NcWaasServerOptions): NcWaasServerClient;NcWaasServerClient
Section titled “NcWaasServerClient”class · server.d.ts
class NcWaasServerClient { constructor(options: NcWaasServerOptions); /** * Verifies an access token from `getAccessToken()` and returns who it belongs to. * JWTs are verified offline (signature, issuer, audience = this tenant's app, expiry). * Legacy session tokens fall back to introspection, which needs a credential. * Anything else throws `TOKEN_INVALID`. */ verifyAuthToken(token: string): Promise<NcWaasAuthTokenClaims>; /** `@privy-io/node` shape: `privy.utils().auth().verifyAccessToken({ access_token })`. */ utils(): { auth: () => { verifyAccessToken: ({ access_token }: { access_token: string; }) => Promise<NcWaasAuthTokenClaims>; }; }; /** * Creates the owner signing request for a contract call that the user then approves in the * browser. Optional: frontends can propose calls themselves within the tenant's policy. */ proposeTransaction(input: ProposeTransactionInput): Promise<{ signingRequestId: string; }>; /** The tenant's wallet by id (`wallets:read`): address, chain, status. No email or other login data. */ getWallet(walletId: string): Promise<TenantWallet>; /** The tenant's user with this email login (`wallets:read`), or null. The match ignores case and surrounding spaces. */ findUserByEmail(email: string): Promise<TenantUser | null>; /** The tenant's user with this wallet address (`wallets:read`), or null. */ findUserByWalletAddress(address: string): Promise<TenantUser | null>;}NcWaasServerOptions
Section titled “NcWaasServerOptions”interface · server.d.ts
interface NcWaasServerOptions { apiUrl: string; tenantId: string; /** Tenant Client Credential. Only for introspection, wallet reads and user lookups (`wallets:read`) and `proposeTransaction` (`owner-intents:create`). */ credentialId?: string; clientSecret?: string; scopes?: Array<"wallets:read" | "owner-intents:create" | "user-intents:create" | "sponsorship:request">; /** Client Credential token audience. Defaults to the API origin. */ audience?: string; /** Expected JWT issuer. Defaults to "urn:nc-waas". */ issuer?: string; /** Defaults to `${apiUrl}/.well-known/jwks.json`. */ jwksUrl?: string; /** Allowed clock skew. Default 30 s. */ clockToleranceSeconds?: number; fetch?: typeof fetch; now?: () => number;}NcWaasAuthTokenClaims
Section titled “NcWaasAuthTokenClaims”interface · server.d.ts
Verified claims. userId equals the browser user.id.
interface NcWaasAuthTokenClaims { appId: string; tenantId: string; tenantInstanceId?: string; userId: string; walletId: string; walletAddress: Hex; chainId: number; sessionId: string; issuer: string; /** Unix seconds. */ issuedAt: number; /** Unix seconds. */ expiration: number; authenticationMethod: string;}ProposeTransactionInput
Section titled “ProposeTransactionInput”interface · server.d.ts
interface ProposeTransactionInput { walletId: string; to: Hex; value: string; data: Hex; title: string; description?: string; requester: { appName: string; origin: string; }; risk?: "low" | "medium" | "high";}linkPrivyUser
Section titled “linkPrivyUser”function · server.d.ts
Builds the user-id mapping record for a dual-login migration. Call it only after verifying
BOTH tokens in the same request: the Privy token with Privy’s own verifier, the Pouchi
token with verifyAuthToken. Linking by email alone would let anyone who controls an
email claim another account. Store the record in your user table.
function linkPrivyUser(input: { privyClaims: { userId: string; }; ncWaasClaims: NcWaasAuthTokenClaims; oldAddress?: string; now?: Date;}): PrivyUserLink;PrivyUserLink
Section titled “PrivyUserLink”interface · server.d.ts
interface PrivyUserLink { privyUserId: string; ncWaasUserId: string; tenantId: string; walletId: string; oldAddress?: Hex; newAddress: Hex; linkedAt: string;}@shard-lab/nc-waas-react/wagmi
Section titled “@shard-lab/nc-waas-react/wagmi”ncWaasConnector
Section titled “ncWaasConnector”function · wagmi.d.ts
function ncWaasConnector(options?: NcWaasConnectorOptions): (config: { emitter: WagmiConnectorEmitter;}) => NcWaasWagmiConnector;NcWaasConnectorOptions
Section titled “NcWaasConnectorOptions”interface · wagmi.d.ts
interface NcWaasConnectorOptions { /** Defaults to the controller of the mounted provider. */ core?: NcWaasCore; name?: string; icon?: string;}