This is one of those things that I *probably* won’t ever need to do again, but just in case I do, I can come back here to remind myself rather than figuring it out all again.
I have a use case where I need to have a Monero view-only wallet, but spending is only possible with a hardware wallet. Normally a hardware wallet won’t tell you your private view key (needed for a view-only wallet) because it tries to save you from yourself and “protect your privacy”.
In my case, I need an automated process to generate new Monero addresses so we know which customer sent funds (the deposit addresses are unique to each customer and we are able to cross-reference the address to the customer on our end). I also want to protect the private keys for the wallet so in a worst-case-scenario where the server was compromised, a hacker couldn’t steal the funds in the wallet.
A standard hardware wallet can’t generate addresses without a human because you need to physically confirm to export the private view key with the hardware wallet (a Ledger in my case). I’m not particularly worried about privacy issues if the server was hacked and the private view key were compromised (it’s for legitimate business purposes and taxes are paid, and if the server was hacked someone seeing how much Monero came in from customers is the least of my concerns).
A few hours of sifting through source code for Ledger’s Monero App, and I was able to reverse engineer the command wallets use to export the private view key (normally kept in memory and not disclosed to users).
I was able to keep the private keys solely in the hardware wallet for spending (always done with a human and the physical wallet), but also able to programmatically generate deposit addresses on the fly without a human by using Ledger’s library to interact with Ledger devices with the following shell command:
echo 0020020001 > cmd.txt;python3 ledgerctl.py send cmd.txt
That command triggers a prompt on your hardware device to export your view key. If you confirm that, it outputs your private view key with 4 extra characters appended to it (don’t care enough to figure out what the purpose of those last 4 characters are, easy enough to just ignore them).
Now you can generate a view-only wallet using that private view key like so:
monero-wallet-cli --generate-from-view-key wallet-view_only