Key Management

Manage the ECDH keypairs used to encrypt and decrypt market maker offers in the RFQ system.

Why Keys Matter

Every RFQ you create includes your ECDH compressed public key. Market makers use it to encrypt their offers so that only you can decrypt them (sealed-bid privacy). The corresponding private key must be available when you want to:

  • Decrypt an offer for early settlement

  • Debug a decryption failure by comparing stored vs on-chain public keys

If you lose your private key, you cannot decrypt any offers made against RFQs that used the corresponding public key. There is no recovery mechanism.


Storage Providers

The SDK automatically selects a storage provider based on your runtime environment:

Environment
Default Provider
Persistence
Location

Node.js

FileStorageProvider

Persistent

.thetanuts-keys/ directory (permissions 0o600)

Browser

LocalStorageProvider

Persistent

Browser localStorage

Testing

MemoryStorageProvider

Lost on exit

In-memory only


Automatic Key Management

The simplest usage: call getOrCreateKeyPair() and the SDK handles everything.


Custom Storage Location (Node.js)

To save keys to a non-default directory, pass a FileStorageProvider at initialization:


Memory Storage (Testing Only)

Use MemoryStorageProvider in tests or CI environments where you don't need persistence:

The SDK logs a warning when MemoryStorageProvider is used, as a reminder that the keys will not persist.


Custom Storage Provider

Implement KeyStorageProvider to store keys in a database, cloud secret manager, or any other backend:


Key Backup Warning

CRITICAL: Back up your RFQ private keys. If lost, you cannot decrypt offers made to your public key. There is no recovery mechanism.

  • Node.js: Keys are in .thetanuts-keys/ with 0o600 permissions — back up this directory

  • Browser: Keys are in localStorage — cleared if the user clears browser data

Export for Backup

Import from Backup


RFQKeyManagerModule Method Reference

Method
Description
Requires Signer

generateKeyPair()

Generate a new ECDH keypair (not stored)

No

getOrCreateKeyPair()

Get from storage, or generate and store a new one

No

loadKeyPair()

Load existing keypair from storage

No

hasStoredKey()

Check whether a key exists in storage

No

storeKeyPair(keypair)

Explicitly save a keypair to storage

No

removeStoredKey()

Delete the stored keypair

No

exportPrivateKey()

Export private key hex string for backup

No

importFromPrivateKey(key, store?)

Import a keypair from a private key hex

No

encryptOffer(amount, nonce, pubKey)

Encrypt an offer for a given public key

No

decryptOffer(data, pubKey)

Decrypt an incoming offer using stored private key

No

generateNonce()

Generate a random nonce

No

getPublicKeyFromPrivate(key)

Derive compressed public key from private key

No

isValidPublicKey(key)

Validate a compressed public key format

No

getStorageKeyId()

Get the storage identifier for the current key

No


Encryption Technical Details

ECDH Key Exchange

The SDK uses secp256k1 ECDH for secure key exchange between requester and market maker:

Nonce Format

The nonce field in decrypted offers can be in two formats depending on the source:

Source
Format
Example

MM Bot

16-char hex string

"987563ef5fde9655"

SDK

Decimal string

"391788778684598574"

The SDK automatically detects and handles both formats during decryption.


Key Mismatch Prevention

Common mistakes that cause "Authentication failed" or "KeyNotFoundError":

  1. Regenerating keys between RFQ creation and decryption — Always use getOrCreateKeyPair(), never generateKeyPair(), for production flows

  2. Running on a different machine without copying .thetanuts-keys/

  3. Using MemoryStorageProvider in a long-running process that restarts

Best practices:

  • Use getOrCreateKeyPair() for all production RFQ flows

  • Back up .thetanuts-keys/ before deploying to new infrastructure

  • Verify the stored key matches the on-chain key before attempting decryption:


Decryption Troubleshooting

"KeyNotFoundError: RFQ key not found"

  • No key in storage for the current keyStorageProvider

  • Check that .thetanuts-keys/ exists (Node.js)

  • If lost: create a new RFQ with a freshly generated key; old RFQ offers cannot be recovered

"DecryptionError: Invalid ciphertext"

  • Wrong private key, or the encrypted offer data is truncated

  • Verify encryptedOffer is complete (from the event log, not truncated)

"DecryptionError: Authentication failed"

  • AES-GCM auth tag verification failed — shared secret is wrong

  • Confirm the stored keypair was used when creating the RFQ (compare public keys)


See Also

Last updated