import nacl from 'tweetnacl'; import bs58 from 'bs58'; export interface KeyPair { publicKey: Uint8Array; secretKey: Uint8Array; } /** Generate an x25519 keypair for Diffie-Hellman key exchange with Phantom. */ export function generateKeyPair(): KeyPair { const kp = nacl.box.keyPair(); return { publicKey: kp.publicKey, secretKey: kp.secretKey }; } /** Derive a shared secret from our secret key and Phantom's public key. */ export function deriveSharedSecret( ourSecretKey: Uint8Array, theirPublicKey: Uint8Array, ): Uint8Array { return nacl.box.before(theirPublicKey, ourSecretKey); } /** Encrypt a payload for Phantom using the shared secret. */ export function encryptPayload( payload: object, sharedSecret: Uint8Array, ): { nonce: string; ciphertext: string } { const nonce = nacl.randomBytes(24); const message = new TextEncoder().encode(JSON.stringify(payload)); const encrypted = nacl.box.after(message, nonce, sharedSecret); if (!encrypted) throw new Error('Encryption failed'); return { nonce: bs58.encode(nonce), ciphertext: bs58.encode(encrypted), }; } /** Decrypt a payload returned by Phantom using the shared secret. */ export function decryptPayload( ciphertextBase58: string, nonceBase58: string, sharedSecret: Uint8Array, ): Record { const ciphertext = bs58.decode(ciphertextBase58); const nonce = bs58.decode(nonceBase58); const decrypted = nacl.box.open.after(ciphertext, nonce, sharedSecret); if (!decrypted) throw new Error('Decryption failed — invalid shared secret or corrupted data'); return JSON.parse(new TextDecoder().decode(decrypted)); }