Cold Wallet Development
Cold storage is keeping private keys in completely internet-isolated environment. 70–90% of exchange reserves should be in cold storage. Major exchange hacks (Mt.Gox, Bitfinex, Binance 2019) showed: hot wallets are main target. Cold wallet built correctly is physically impossible to hack remotely.
Cold Storage Principles
Air-Gapped System
Air gap is physical isolation: computer for signing transactions never connects to internet. Even in "off" state — WiFi and Bluetooth disabled or physically removed.
Transaction signing workflow:
Online Server → creates unsigned tx → USB/QR → Air-gapped PC → signs → USB/QR → Online Server → broadcast
QR codes preferable to USB (USB can be BadUSB attack vector). Animated QR (BBQR format) allow transferring large data through QR sequences.
Hardware Wallets
Ledger and Trezor are consumer-grade HSM. Suitable for warm wallet and operators with small volumes. Private key created and stored inside device, never exported.
# Ledger integration via ledgerblue
from ledgerblue.comm import getDongle
def sign_eth_transaction_with_ledger(unsigned_tx: bytes, bip44_path: str) -> bytes:
dongle = getDongle(debug=False)
# BIP44 path: m/44'/60'/0'/0/0 for first Ethereum address
path_bytes = parse_bip44_path(bip44_path)
# Send transaction to device for signing
apdu = build_sign_apdu(path_bytes, unsigned_tx)
result = dongle.exchange(apdu)
# Extract v, r, s from response
v = result[0]
r = result[1:33]
s = result[33:65]
return encode_signature(v, r, s)
For enterprise — Thales Luna HSM, AWS CloudHSM, YubiHSM. Hardware protection with FIPS 140-2 certification, multi-party computation (MPC) support.
Multi-Signature Wallets
One person with one key is single point of failure. Multi-sig requires M of N signatures for transaction.
Bitcoin P2SH Multisig
def create_multisig_address(public_keys: list[bytes], m: int) -> tuple[str, bytes]:
# Create M-of-N P2SH multisig address
# m: required signatures
# public_keys: list of public keys (N total)
# Redeem script: OP_M <key1> <key2> ... <keyN> OP_N OP_CHECKMULTISIG
redeem_script = script.multisig(m, public_keys)
# P2SH address = HASH160(redeem_script)
p2sh_address = script.p2sh_address(redeem_script)
return p2sh_address, redeem_script
# Example: 2-of-3 (2 of 3 keys must sign)
# key1 — CEO (hardware wallet in office)
# key2 — CTO (hardware wallet at home)
# key3 — CFO (hardware wallet in bank vault)
address, redeem_script = create_multisig_address([key1, key2, key3], m=2)
Ethereum Gnosis Safe
Gnosis Safe is de facto standard for Ethereum multisig. Smart contract wallet with M-of-N support, time locks, delegate calls.
import Safe, { EthersAdapter } from '@safe-global/protocol-kit';
import { ethers } from 'ethers';
async function createEnterpriseMultisig(
owners: string[], // owner addresses
threshold: number // M of N
): Promise<string> {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const deployer = new ethers.Wallet(DEPLOYER_KEY, provider);
const ethAdapter = new EthersAdapter({ ethers, signerOrProvider: deployer });
const safeFactory = await SafeFactory.create({ ethAdapter });
const safeAccountConfig = {
owners,
threshold,
};
const safe = await safeFactory.deploySafe({ safeAccountConfig });
console.log('Safe deployed at:', await safe.getAddress());
return safe.getAddress();
}
Cold Wallet Access Procedures
Documented SOP (Standard Operating Procedure)
Each cold storage withdrawal must follow documented procedure:
COLD STORAGE WITHDRAWAL PROCEDURE
1. INITIATION (by one of 2 authorized employees)
- Create ticket with amount, purpose, business justification
- Get approval from second authorized employee
2. TRANSACTION PREPARATION (CTO or authorized technical staff)
- On online machine: create unsigned transaction
- Write to USB (data only, no executables) or print QR
3. SIGNING (in presence of 2 witnesses)
- In isolated room, no phones or cameras
- Load unsigned tx on air-gapped PC
- Verify address and amount on hardware wallet display
- Sign
- Transfer signed tx to online machine via USB/QR
4. BROADCAST (after final verification)
- Check recipient address once more
- Broadcast transaction
- Record TxHash in audit log
5. CONFIRMATION
- Wait for N block confirmations
- Update internal balance
- Close ticket with TxHash
Each step logged with timestamp and participant signatures.
Time Locks
For additional protection — time lock on large transfers. Gnosis Safe supports through TimelockController:
// Any withdrawal > 100 ETH executed only 48 hours after proposing
uint256 public constant MIN_DELAY = 48 hours;
uint256 public constant MAX_WITHDRAWAL = 100 ether;
function scheduleWithdrawal(address to, uint256 amount) external onlyOwner {
require(amount > MAX_WITHDRAWAL ? block.timestamp + MIN_DELAY : block.timestamp,
"Immediate for small amounts");
// schedule through TimelockController
}
If keys compromised — team has 48 hours to notice suspicious transaction and cancel it.
Key Storage Scheme
3-2-1 Rule: 3 copies of key, 2 different media, 1 offsite.
For exchange:
- 2-of-3 Gnosis Safe for Ethereum reserves
- 2-of-3 Bitcoin multisig (P2WSH) for Bitcoin reserves
- Keys held by: CEO (HSM in office), CTO (HSM at home), independent custodian (HSM in bank vault)
- Seed phrases on metal backup (Cryptosteel, Bilodeau) — fireproof and waterproof
- Encrypted digital copies in different jurisdictions (AWS S3 with SSE-KMS + EncryptedLocal)
Storage Structure:
Hot Wallet: 5–10% of reserves (automatic withdrawals)
Warm Wallet: 15–20% of reserves (2-of-3 multisig, tops up hot wallet)
Cold Storage: 70–80% of reserves (air-gapped, manual withdrawal per SOP)
Audit and Reserves Verification
Proof of Reserves — public proof that exchange owns claimed funds:
def generate_proof_of_reserves(exchange_addresses: list[str],
user_balances_merkle: bytes) -> ProofOfReserves:
# Merkle tree of user balances.
# Exchange publishes:
# 1. Merkle root of all balances
# 2. On-chain balances (verifiable publicly)
# 3. Each user can verify their inclusion in Merkle tree
total_user_balances = sum_all_user_balances()
merkle_root = build_merkle_tree(user_balances_merkle)
chain_balances = get_onchain_balances(exchange_addresses)
assert chain_balances >= total_user_balances, "Exchange is insolvent!"
return ProofOfReserves(
merkle_root=merkle_root,
total_liabilities=total_user_balances,
total_assets=chain_balances,
timestamp=datetime.utcnow(),
auditor_signature=sign_with_auditor_key(merkle_root),
)
Development Timeline
| Component | Timeline |
|---|---|
| Gnosis Safe multi-sig setup | 1 week |
| Bitcoin P2WSH multisig | 2–3 weeks |
| SOP documentation + procedures | 1–2 weeks |
| Air-gapped signing workflow | 2–3 weeks |
| Proof of Reserves implementation | 2–3 weeks |
| Security audit | 2–4 weeks |
Full cold storage system with documented procedures and Proof of Reserves: 2–3 months.







