Skip to content
PricingDocsKO

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).

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

function · react/provider.d.ts

Privy-compatible name. Migration also requires the Pouchi API URL, app ID and chain configuration.

const PrivyProvider: typeof NcWaasProvider;

function · react/provider.d.ts

function NcWaasProvider({ apiUrl, tenantId, appId, chain, config, core: suppliedCore, children }: NcWaasProviderProps): import("react").JSX.Element;

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;
}

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;
};
}

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";
}

function · react/hooks.d.ts

function usePrivy(): UsePrivyResult;

type · react/hooks.d.ts

type UsePrivyResult = UsePrivyCore & ReturnType<typeof useUnsupportedUnlinks> & ReturnType<typeof useUnsupportedLinks>;

function · react/hooks.d.ts

function useLogin(callbacks?: {
onComplete?: (params: LoginCompleteParams) => void;
onError?: (error: NcWaasError) => void;
}): {
login: () => void;
};

interface · react/hooks.d.ts

interface LoginCompleteParams {
user: User;
isNewUser: boolean;
wasAlreadyAuthenticated: boolean;
loginMethod: LoginMethodName | null;
loginAccount: LinkedAccount | null;
}

function · react/hooks.d.ts

function useLogout(callbacks?: {
onSuccess?: () => void;
}): {
logout: () => Promise<void>;
};

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"];
};
}

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;
}

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;
};

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;
};

interface · react/hooks.d.ts

interface LinkAccountSuccess {
user: User;
linkMethod: LoginMethodName;
linkedAccount: LinkedAccount | null;
}

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;
};
};

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";
};
};

function · react/hooks.d.ts

function useWallets(): {
ready: boolean;
wallets: 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>;
}

function · react/hooks.d.ts

One EIP-1193 provider per controller, shared by every wallet object.

function getEthereumProviderFor(core: NcWaasCore): NcWaasEthereumProvider;

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">>;
};

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;
}

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;
}>;
};

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;
}>;
};

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>;
};

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>;
};

type · types.d.ts

type TransactionProposer = (input: TransactionProposalInput) => Promise<{
signingRequestId: string;
}>;

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;
}

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;
}

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;
}

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;
};

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[];
}

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;
}

interface · session-approvals.d.ts

interface SessionApprovalState {
policyId: string;
expiresAt: number;
maxValuePerCallWei: string;
valueAllowanceWei: string;
spentWei: string;
callsRemaining: number;
permissions: SessionPermission[];
}

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>;
};

function · react/hooks.d.ts

Alias in Privy-style naming.

const useRecoverWallet: typeof useWalletRecovery;

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;
};

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>;
};

function · react/devices.d.ts

Signs one other device out (Privy-style naming).

function useRevokeSession(): {
revokeSession: (deviceId: string | null) => Promise<void>;
};

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;

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;

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[]>;
};

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).

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>;
};

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>;
};

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>;
};

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;
};

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>;
};

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>;
};

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>;
};

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;
};

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;
};

function · react/provider.d.ts

The controller, for custom UIs (headless login and approval).

function useNcWaas(): NcWaasCore;

function · react/provider.d.ts

The raw controller state. Prefer the Privy-compatible hooks for app code.

function useNcWaasState(): NcWaasState;

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;
});
}

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";

Server only. Keep it out of browser bundles.

function · server.d.ts

Privy new PrivyClient(...) counterpart.

function createNcWaasServerClient(options: NcWaasServerOptions): 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>;
}

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;
}

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;
}

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";
}

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;

interface · server.d.ts

interface PrivyUserLink {
privyUserId: string;
ncWaasUserId: string;
tenantId: string;
walletId: string;
oldAddress?: Hex;
newAddress: Hex;
linkedAt: string;
}

function · wagmi.d.ts

function ncWaasConnector(options?: NcWaasConnectorOptions): (config: {
emitter: WagmiConnectorEmitter;
}) => NcWaasWagmiConnector;

interface · wagmi.d.ts

interface NcWaasConnectorOptions {
/** Defaults to the controller of the mounted provider. */
core?: NcWaasCore;
name?: string;
icon?: string;
}