Custodial wallets
Creation
On registration, auth.service.register:
- Generates a random Stellar keypair.
- Encrypts the secret with
encryptSecret(AES-256-GCM,ENCRYPTION_KEY). - Stores
stellarPublicKeyandstellarSecretEncon the user. - On testnet, calls Friendbot in the background (failures are logged, not fatal).
Encryption format
base64(iv) . base64(authTag) . base64(ciphertext)- 12-byte random IV per encryption
- 16-byte GCM auth tag — tampering causes decryption to throw
- Implemented in
src/lib/crypto.js, wrapped with the app key insrc/lib/secrets.js
Using the secret
Secrets are decrypted only immediately before signing:
lockEscrow— seller funds escrowsendXlm— withdrawals
They are never logged, returned by the API, or held in long-lived variables.
Withdrawals
wallet.service.withdraw:
- Loads the user's encrypted secret.
- Computes
withdrawable = available − committedToSellOrders. - Rejects amounts above that with
422 INSUFFICIENT_BALANCE. - Sends a
payment, orcreateAccountif the destination doesn't exist (≥ 1 XLM required). - Records a
WITHDRAWALtransaction.
Roadmap
Phase 2 adds external wallet connections, where users sign escrow locks with their own keys.