@solana/keychain-privy
Privy-based signer for Solana transactions using Privy's wallet API.
Installation
pnpm add @solana/keychain-privyUsage
Create and Initialize
import { createPrivySigner } from '@solana/keychain-privy';
// Create and initialize the signer (fetches public key from Privy API)
const signer = await createPrivySigner({
appId: 'your-privy-app-id',
appSecret: 'your-privy-app-secret',
walletId: 'user-wallet-id',
});
console.log('Signer address:', signer.address);Sign Messages
import { createSignableMessage } from '@solana/signers';
const message = createSignableMessage('Hello, Privy!');
const [signatures] = await signer.signMessages([message]);Sign Transactions
// Sign transactions using Privy's API
const [signatures] = await signer.signTransactions([transaction]);Authorization Context
For Privy wallets that enforce authorization controls, pass an authorization context. The signer mirrors the REST signing behavior used by Privy's SDK for wallet RPC requests: it signs the exact POST /wallets/{walletId}/rpc body and injects privy-authorization-signature plus privy-request-expiry.
const signer = await createPrivySigner({
appId: 'your-privy-app-id',
appSecret: 'your-privy-app-secret',
walletId: 'user-wallet-id',
authorizationContext: {
authorization_private_keys: ['wallet-auth:...'],
},
});authorization_private_keys uses Node.js node:crypto support to parse and sign with P-256 keys. In browser, Cloudflare Workers, or Deno runtimes without Node compatibility, use sign_fns instead.
You can also use sign_fns when signing must happen in another system such as KMS, passkeys, or a service that performs Privy's JWT exchange:
const signer = await createPrivySigner({
appId: 'your-privy-app-id',
appSecret: 'your-privy-app-secret',
walletId: 'user-wallet-id',
authorizationContext: {
sign_fns: [async payload => signPayloadAndReturnBase64DerSignature(payload)],
},
});This package intentionally does not implement Privy's user_jwts exchange flow, because that requires HPKE key exchange and caching. Use sign_fns for that path.
Check Availability
const available = await signer.isAvailable();
console.log('Privy API available:', available);API Reference
createPrivySigner(config)
Creates and initializes a new PrivySigner instance.
Config options:
appId(string, required): Your Privy application IDappSecret(string, required): Your Privy application secretwalletId(string, required): The Privy wallet ID to use for signingapiBaseUrl(string, optional): Custom API base URL (defaults tohttps://api.privy.io/v1)requestDelayMs(number, optional): Delay in ms between concurrent signing requests to avoid rate limits (default: 0)authorizationContext(object or function, optional): Privy wallet authorization context withauthorization_private_keys,signatures, orsign_fnsauthorizationRequestExpiryMs(number or null, optional): Request-expiry window for authorization signatures (defaults to 15 minutes when authorization context is configured; set tonullto omit)
Example:
Returns: Promise<PrivySigner>
Methods
signMessages(messages): Signs one or more messagessignTransactions(transactions): Signs one or more transactionsisAvailable(): Checks if the Privy API is reachableaddress: Read-only property containing the signer's Solana address
How It Works
- Initialization: Fetches the wallet's public key from Privy API during creation
- Signing: Sends transactions/messages to Privy's signing API endpoint
- Extraction: Uses
extractSignatureFromWireTransactionto extract signatures from Privy's response
Security Notes
- Store your
appSecretsecurely (use environment variables) - Never expose your
appSecretin client-side code - This signer is intended for server-side use or secure environments
- Privy handles key management - your private keys never leave Privy's infrastructure