No. 09Beenzer
A signature instead of a password
Beenzer has no passwords. You sign in by signing a message in Phantom, and you pay for a mint with a transfer the server reads back from the chain before it mints anything. Here is what each check stops.
Vincent Porret
Co-founder and CEO · · 3 min read
Beenzer pins photos and videos to the place they were taken, and any post can be minted as an NFT on Solana. So the app has to answer two questions a bank would recognise at once: who are you, and have you paid? It answers both without a password, and without ever holding a key of yours. This is how the React Native app and its server do it, as we rebuild the app in Flutter.
A message, not a password
Your wallet is your account: users are keyed by their wallet’s public key, and the username comes after. To sign in, the app asks the server for a message, and the server writes a fresh one:
const nonce = randomBytes(16).toString("hex");
const message = `Sign in to Beenzer\n\nWallet: ${pubkey}\nNonce: ${nonce}\nIssued: ${new Date().toISOString()}`;The app opens Phantom with a deep link, Phantom shows the message as plain text, and you sign it. The server then checks the signature against your public key, an Ed25519 verification with tweetnacl. The nonce is good for five minutes, and it is deleted as soon as a signature has been checked against it, valid or not: a signature that leaks cannot be replayed, and each nonce buys one try. A connection can ask for a nonce, or try to sign in, five times in ten seconds.
What comes back is a session: 32 random bytes, good for 30 days. The server stores only their SHA-256 hash, so a copy of its database lets nobody in. On the phone, the token is kept in the system’s secure storage.
The server keeps the hash of your session, never the session itself.
Paid once
A mint costs a fixed fee in SOL, 0.01 by default. The app builds a plain transfer to Beenzer’s market wallet, with you as the fee payer, and Phantom signs it. The server does not take that on trust. Before relaying the transaction, it checks that you are its fee payer, that every instruction in it is a System transfer to the market wallet, apart from optional compute-budget ones, and that its signatures are valid.
Before minting, it reads the transaction back from the chain, at confirmed commitment, and checks that it succeeded and that what reached the market wallet from you covers the fee. Then it writes the transaction’s signature into a payments table, where the signature is the primary key. The same payment cannot buy two mints: the second time, the database refuses it.
Two wallets, two jobs
Revenue and costs never share a wallet. Fees land in the market wallet. A separate master wallet pays for everything a mint costs: the upload of the media to Arweave, a three-second GIF preview when the post is a video, the metadata, then the mint itself and the print of the first edition to its creator. Mints run one at a time, in a queue.
One gap is written in the code itself. If a mint fails after its payment is recorded, nothing refunds it automatically: the comment says it needs a manual refund or retry, and the app tells you to contact support. I would rather write that down than pretend it does not happen.
The royalty, in the token
Each post is minted with Metaplex as a programmable NFT, in up to 1,000 editions on the server’s side; the app’s slider stops at 50. Resales carry a 10% royalty, written into the token’s metadata as a list of creators and their shares: 80% of the royalty to the creator, 20% to Beenzer. The token follows the rule set Metaplex publishes by default to enforce royalties.
None of this asks for an email or a password, and none of it holds a key of yours. The server signs with its own wallets. Yours stays in Phantom.
Vincent

