Export Private Key
You can export the decrypted private key from a server wallet. This is useful for migration or backup scenarios.
const decryptedPrivateKey = await client.exportPrivateKey({
organizationId: 'fdbbad46-047f-4ff7-a13c-9e14bca69857',
address: '0x5bBF0b7847A7a35E419eD431069fE92542B4f5c3',
embeddedPrivateKey: process.env.SERVER_WALLET_ACCESS_KEY!,
blockchain: 'evm',
});
| Parameter | Type | Description |
|---|
organizationId | string | Organization ID from your authenticated session |
address | string | Wallet address to export |
embeddedPrivateKey | string | Your 64-char hex access key |
blockchain | string | 'evm' or 'solana' |
The exported key gives full control over the wallet. Handle it with extreme care — encrypt it at rest, never log it, and never transmit it over insecure channels.
Error Handling
The SDK throws typed errors that preserve the backend’s error code and message. Wrap your calls in try/catch to handle them gracefully:
import {
ValidationError,
UnauthorizedError,
ConflictError,
} from '@abstraxn/server-signer';
try {
const session = await client.authenticate({
userIdentity: 'merchant-backend-user-001',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
} catch (error) {
if (error instanceof ConflictError) {
// 409: identity exists with a different access key
console.error('Access key mismatch for this identity');
} else if (error instanceof UnauthorizedError) {
// 401: token expired and refresh failed
console.error('Authentication failed');
} else if (error instanceof ValidationError) {
// 400: missing or invalid parameters
console.error('Invalid input:', error.message);
} else {
throw error;
}
}
Common Error Codes
| Status | Error Type | When It Happens |
|---|
400 | ValidationError | Missing required fields (e.g. stamped payload fields for whoami or signTransaction) |
401 | UnauthorizedError | Token missing or expired, and automatic refresh failed |
409 | ConflictError | userIdentity already registered with a different access key or public key |
MFA (Multi-Factor Authentication)
The SDK includes MFA methods for additional security on sensitive operations:
// Check MFA status
const status = await client.mfaStatus();
// Enable MFA
await client.mfaEnable();
// Verify MFA setup with a TOTP code
await client.mfaVerifySetup({ code: '123456' });
// Verify MFA for a standard operation
await client.mfaVerify({ code: '123456' });
// Verify MFA specifically for signing
await client.mfaVerifySign({ code: '123456' });
// Disable MFA
await client.mfaDisable({ code: '123456' });
Security Best Practices
Store secrets securely
Never hardcode accessKey or API keys in source code. Use a secrets manager:
// ✅ Good — loaded from environment / secrets manager
const client = new ServerSignerClient({
apiKey: process.env.ABSTRAXN_API_KEY!,
});
const session = await client.authenticate({
userIdentity: 'payout-service',
accessKey: process.env.SERVER_WALLET_ACCESS_KEY,
});
// ❌ Bad — hardcoded secrets
const client = new ServerSignerClient({
apiKey: 'ak_live_abc123...',
});
Never expose keys in frontend code
The @abstraxn/server-signer package is designed for Node.js backends. If you need wallet functionality in a browser, use @abstraxn/signer-react instead.
Avoid logging sensitive data
Do not log raw access keys, private keys, or full signatures in production:
// ❌ Bad
console.log('Access key:', session.accessKey);
console.log('Private key:', decryptedPrivateKey);
// ✅ Good — log only non-sensitive identifiers
console.log('Authenticated identity:', session.userIdentity);
console.log('Wallet address:', session.walletAddress);
Rotate and audit
- Rotate API keys periodically from the Abstraxn Dashboard
- Monitor your wallet transactions for unexpected activity
- Use separate
userIdentity values for different services (e.g. payout-service, minting-service)
Deprecated Aliases
These methods still work but will be removed in a future version:
| Deprecated | Use Instead |
|---|
ensureServerWalletSession(...) | authenticate(...) |
createViemClient(...) | createPublicClient(...) |
Next Steps