How I made crypto payments work with a username instead of a 42-character address
I'm a solo developer from Tajikistan. Every time I watched someone send USDT, the ritual was the same: copy a 42-character address, paste it, compare the first four and last four characters, hesitate, compare again. One
I'm a solo developer from Tajikistan. Every time I watched someone send USDT, the ritual was the same: copy a 42-character address, paste it, compare the first four and last four characters, hesitate, compare again. One wrong character and the money is gone.
So I built NoN Wallet, a self-custody wallet where you send money to a name. Here's how it works under the hood, and what I learned building it.
1. Creating a wallet: a name and a password
Most wallets open with "write down these 12 words". For a lot of people that's where they quit. In NoN Wallet you type a name and a password, and that's it:
- the 12-word phrase is generated silently in the browser;
- the vault is encrypted with your password (PBKDF2-SHA256, 600,000 rounds, AES-GCM) and stays on the device;
- the 12 words are still there, in Settings, behind Face ID or the password.
2. Names live on-chain, and I pay the gas
Names are stored in a small contract on BNB Chain: claim(string) binds a name to your address. Anyone can read it, so a name keeps working even if my server disappears.
The catch: a brand-new wallet has zero BNB, so it can't pay gas to claim a name. I solved this with MegaFuel, BNB Chain's gas sponsorship (a paymaster for ordinary transactions):
- The wallet signs a normal transaction with
gasPrice: 0. - A tiny relay forwards it to MegaFuel.
- MegaFuel pays the fee, but only if the transaction matches my sponsor policy.
The policy whitelists exactly one contract and one method (claim(string)), with per-address limits. One registration costs me about half a cent. The relay holds no keys and no funds; it only forwards four JSON-RPC methods and rate-limits by IP.
Sending by name then is just a lookup: type muslihiddin, the wallet resolves the address on-chain and shows you who the money is going to before you confirm. It works on BNB Chain, Ethereum and Base, since EVM addresses are the same there.
3. Backups to iCloud or Google Drive, without me storing anything
A phrase on paper is great until the paper is lost. So right after creating a wallet, the app offers to save an encrypted backup file:
- on the web, via the Web Share API with a file (
navigator.share({ files })), which opens the system sheet with "Save to Files", iCloud Drive and Google Drive; - in the Android app (Capacitor), the WebView has no share API, so a small native plugin writes the file to the app cache and opens the Android share sheet through a
FileProvider.
The file is useless without the password, and I never see either. Until you save a backup, the wallet shows a reminder.
4. Boring tech on purpose
- The wallet is plain HTML and JavaScript, no build step, no framework.
- The Android app is a Capacitor shell built with Gradle only: no Google Play services, no trackers.
- The APK is published with its SHA-256 and signing certificate, and the deploy refuses to ship a file whose hash doesn't match.
- Everything is open source under MIT.
What I'd love feedback on
- Would you trust a "name + password" flow for a self-custody wallet?
- Is anything in the security model weaker than it should be?
- What's confusing the first time you open it?
Code: https://github.com/tavarovwallet/tavarov
Try it in the browser: https://wallet.tavarov.com
Thanks for reading. I'm happy to answer anything in the comments.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.