Skip to main content

Encrypt wallet in memory

ChainGate's in-memory encryption safeguards private keys and recovery phrases while your wallet application is active. This security layer requires user authentication whenever sensitive data is accessed, preventing unauthorized use if your application's memory is compromised.

Note

In-memory encryption is mainly relevant for browser or apps. If you're working in a secure backend environment, you might not need it — see When is in-memory encryption needed? below.

Real-World Example

Think of encrypting your wallet like buying a new safe: you set a combination (password), and the safe always requires it. Even if someone inspects your device's memory, they can't access your private key or recovery phrase without that password.

Encrypt your wallet

To enable in-memory encryption, call wallet.encrypt() after creating or importing a wallet. You provide a password and an askForPassword callback that the library will invoke whenever it needs to access secret material (e.g., when signing a transaction).

import { importWallet } from 'chaingate'

const wallet = importWallet({ phrase: 'your recovery phrase here...' })

// Encrypt the wallet
await wallet.encrypt('Tr0ub4dor&3', async () => {
return prompt('Enter your wallet password:')
})

The askForPassword callback should return:

  • The password string if the user provides it.
  • null to cancel the operation (throws DecryptionCancelledError).
Never log passwords

Never store or log user passwords in plain text. This can expose them to attackers and completely undermine your wallet's security.

Example: Signing and broadcasting a Bitcoin transaction

import { importWallet, ChainGate } from 'chaingate'

const wallet = importWallet({ phrase: 'your phrase here...' })

// Encrypt the wallet
await wallet.encrypt('my-password', async () => {
return prompt('Enter password:')
})

const cg = new ChainGate({ apiKey: 'your-api-key' })
const bitcoin = cg.connect(cg.networks.bitcoin, wallet)

// Create a transfer
const amount = cg.networks.bitcoin.amount('0.001')
const tx = await bitcoin.transfer(amount, 'bc1q...')

// Signing requires the private key — askForPassword() is called
tx.setFee('normal')
const broadcasted = await tx.signAndBroadcast()
console.log('TX ID:', broadcasted.transactionId)
Password prompt

During signAndBroadcast(), your askForPassword function will be invoked if the private key is not currently decrypted. Once the correct password is entered, the transaction is signed and sent to the network.

Check encryption status

console.log(wallet.encrypted) // true or false

Automatic caching for non-sensitive data

ChainGate reduces repeated prompts by caching non-sensitive data (e.g., public keys, addresses, derivation paths) once decrypted:

  • You typically only enter your password once per session or until the system deems it necessary to re-prompt.
  • Public information is stored in a readily accessible manner, while private data (like private keys) always requires decryption.

This design strikes a balance between user experience and security:

  • You won't be prompted for a password every time you ask for an address or public key.
  • You will be prompted whenever the private key or recovery phrase is directly accessed (e.g., signing a transaction).

When is in-memory encryption needed?

Use it in browser-based applications or apps

In browser environments or apps, memory is more exposed:

  • Malicious browser extensions
  • Compromised third-party scripts
  • Memory inspection tools
  • Shared access on a compromised machine

In these cases, sensitive data stored in memory (like private keys) could be at risk. In-memory encryption protects against this by requiring a password before anything critical is decrypted.

Not required in most backend environments

If you're using ChainGate in a backend environment (e.g., a Node.js server or cloud function with restricted access):

  • Only your own trusted code runs
  • The OS and runtime prevent outside access to memory
  • Users never interact directly with the server's memory

In such environments, you typically don't need in-memory encryption, and omitting it can simplify your implementation. However, if untrusted plugins, sandboxed logic, or shared runtimes are involved, adding this layer may still offer useful protection.

Key takeaways

  • wallet.encrypt(password, askForPassword): Encrypts the wallet's secret material in memory.
  • Secure defaults: ChainGate uses AES-256-GCM and PBKDF2 under the hood, providing robust encryption for sensitive data in memory.
  • Convenient caching: Public data (e.g., addresses) is cached to reduce how often the password is needed, without compromising security for private data.
  • Safeguard your password: Always choose a strong password and handle user input securely — never log or expose the entered password.
  • Error handling: AlreadyEncryptedError is thrown if you try to encrypt a wallet that is already encrypted. DecryptionCancelledError is thrown if askForPassword returns null.