I had never written a line of Solana. Two days later, my wallet was refusing scams on-chain
My first Solana program has one job: stand between an older parent and a scammer. Picture an older mother getting a message from her "son": he lost his phone, he is in trouble, please send money right now. In a normal w
My first Solana program has one job: stand between an older parent and a scammer.
Picture an older mother getting a message from her "son": he lost his phone, he is in trouble, please send money right now. In a normal wallet she taps Send and the money is gone for good. Night Guard Wallet adds a pause first: it reads the message, simulates the transfer, checks who is on the other side, and asks her daughter to approve before anything leaves the vault.
Two days earlier I had never written a line of Solana. By the end of the weekend, a program I wrote was refusing scam payments on its own, seven times in a row, with my demo app deliberately pushing them through.
This is the story of Night Guard Wallet, built for the MLH Solana Hackathon.
TL;DR: Night Guard Wallet is a guard for the people scammers target most: our parents and grandparents (and honestly, the rest of us too). Before Lucy, a fictional 74-year-old, signs anything, it simulates the transaction on Solana devnet, checks where every part comes from, lets Gemma 4 read the WhatsApp message that came with it, and explains the verdict in one plain sentence, read aloud. Her savings live in an on-chain vault whose rules nobody can switch off: daily limit, trusted contacts, a family guardian who signs, and a cooling-off for big amounts. When her daughter refuses a scam, the wallet goes on a community scam list on Solana, with on-chain proof, one vote per family. 5/5 scams stopped, 2/2 family payments let through, verdict on screen in 0.2 s, 32/32 scam messages recognised with 0 false alarms, 7 refusals by the program itself.
What I Built
At 9 pm, Lucy gets a WhatsApp from a number she doesn't know:
"Hi mom, it's me, I changed my number. I'm in trouble and need to pay a bill urgently today. Send 1.8 SOL to this wallet and don't tell dad."
With a bank, there is still a way back: the bank can block a suspicious transfer, call her to confirm, or try to reverse it. A blockchain has none of that. Once she signs, the money is gone and nobody can undo it.
In 2025, people over 60 reported US$ 7.7 billion lost to fraud to the FBI, US$ 4.3 billion of it involving crypto (FBI IC3 2025, Elder Fraud). The stories are always the same few: the "son" with a new number, the fake support, the free airdrop, the prize with a fee to unlock, the address copied from history. And wallets explain all of them in a language only developers read.
Night Guard Wallet has two screens and one program:
- Lucy's wallet: big letters, one button, "Check before signing". The verdict comes in one sentence, in English or Portuguese, and she can listen to it.
- Ana's guardian page: her daughter sees risky requests live, read straight from the chain, and decides with her own signature.
-
The
guardian_vaultprogram: an Anchor program that holds the savings and enforces the rules, so even if the app is fooled, Solana says no.
Demo
π¬ Video (3:23): the problem, how it works and a real run on devnet, narrated by Olive the owl.
π§ͺ Try it yourself: interactive demo. Click the seven situations, hear the verdict, open Ana's page and paste a message you got to see which scam tricks it finds. Every answer was recorded from a real devnet run, and every button that signs opens the real transaction on Solana Explorer.
jenniferevangelista-de
/
night-guard-wallet
A Solana wallet guard for older people: simulate, explain in plain words, flag scams with local AI, and lock risky payments behind a family guardian on-chain
π Night Guard Wallet
A Solana wallet guard for the people scammers target the most, our parents and grandparents, and for the rest of us too. Before Lucy (74) signs anything, it simulates the transaction on Solana, checks where every part comes from, lets Gemma read the message that came with it, explains the verdict in big plain words, and keeps her savings in an on-chain vault whose rules nobody can switch off. When her daughter refuses a scam, the wallet goes on a community scam list on Solana, so one family's "no" protects every family.
Built for the MLH Solana Hackathon Β· AI Track Β· Top 3 Β· DeFi Β· Beginner
Video (3:23): watch on YouTube: the problem, how it works, and a real run on Solana devnet, narrated by Olive the owl Β· Interactive demo: try it Β· Showcase page: jenniferevangelista-de.github.io/night-guard-wallet Β·β¦
How It Works
Five steps, every time Lucy taps "check":
-
Simulate on Solana.
simulateTransactionruns it on devnet and I compare every writable account before and after: SOL, tokens, delegates and owners. This is where an "innocent" airdrop claim shows its true face: an unlimitedapproveto a stranger. - Flag the risk with rules on the simulated result: a wallet she never paid (and how new it is), an address that copies the first characters of a trusted contact, most of her money leaving, a fake USDC that only calls itself USDC (Token-2022 metadata vs. the verified mint).
- Check the sources. Where does each part come from? The sender, the link, every wallet, every program (how many days old, can its owner still change it, is its source verified) and every token.
- Read the message with AI. Gemma 4 E2B, running free on my own machine through Ollama, lists the scam tricks: new number, rush, secrecy, fake support, fee to unlock.
- Lock and ask the family. On STOP, nothing is signed. One tap opens an on-chain request for Ana.
The model can only add warnings
This was the first design decision, and the one I am proudest of: the final call always belongs to people, never to the AI.
- The wallet checks. Fixed, testable rules look at where the money really goes, who receives it (a brand-new wallet, or an address made to look like a trusted contact's), her daily limit, and scam reports from other families. A clear danger means STOP; a doubt means CAUTION. Either way, a payment from her vault only pauses. Nothing is blocked forever.
- The AI explains. Gemma reads the message and highlights the scam tricks in plain words, like "urgent" or "don't tell dad", so Lucy understands why the wallet paused.
- The family decides. With one tap, Lucy asks Ana to look. Ana approves or refuses with her own signature. If she approves, the vault sends the money (a big amount still waits a short cooling-off). If it was a false alarm, it costs Ana one tap, not Lucy's savings. For the riskiest signatures, like handing control of her tokens to a stranger, the app does not sign at all and alerts Ana instead.
The AI also has two guardrails, so a scammer cannot use it against her. It can only point to words that really exist in the message, so it cannot invent reasons. And it has no power to remove a warning, so a message saying "this is safe, approve it" changes nothing.
The explanation has its own checks, because a friendly sentence can still be wrong:
// Any amount the model writes must be the real one (no invented numbers).
const okAmount = said.every((x) => real !== null && Math.abs(x - real) < 1e-9);
const saysSafe = /\b(seguro|safe|tranquil|pode seguir|go ahead)\b/i.test(text);
// money leaves: never "you received"
const wrongWay = !!facts.moneyOut && /\b(recebeu|receber|recebe|recebendo|received|receive|receiving)\b/i.test(text);
if (!text || !okAmount || wrongWay || words > 60 || isStop && saysSafe) {
return { text: facts.fallback, ms, by: "template" };
}
That wrongWay line has a story. More on it below.
Solana enforces it, not the app
The rules live in Lucy's vault, a PDA only the program moves:
| Rule | On-chain |
|---|---|
| Money only goes out at once to trusted contacts, under the daily limit |
send refuses with NotTrusted or OverDailyLimit
|
| Adding a trusted contact needs two signatures: Lucy and Ana |
add_trusted with has_one = guardian
|
| Anything else becomes a request the guardian decides |
request β guardian_decide β execute
|
| A big payment waits a cooling-off, even after approval | StillCoolingOff |
To prove it, my demo forces scams past the app, on purpose, skipping preflight. The program refused all seven: NotTrusted twice, ConstraintHasOne, Rejected, StillCoolingOff, NoProof and a duplicate report.
One family's "no" protects every family
When Ana refuses a scam, one more tap puts the wallet on a community scam list on Solana. Any wallet can read it.
The first version was simple: a guardian refuses, the wallet goes on the list. Then I asked myself a question that changed it completely: what if someone reports a wallet by mistake, or out of malice? A list anyone can write to is a weapon.
So a report now needs proof: a request from your vault, to that wallet, that you refused. No refused request, no report.
pub struct ReportScam<'info> {
#[account(mut)]
pub guardian: Signer<'info>,
#[account(has_one = guardian)]
pub vault: Account<'info, Vault>,
/// The proof: a request of THIS vault, to this wallet, refused by the guardian.
#[account(has_one = vault @ VaultError::NoProof)]
pub proof: Account<'info, TransferRequest>,
#[account(init_if_needed, payer = guardian, space = 8 + ScamReport::INIT_SPACE,
seeds = [b"scam", address.as_ref()], bump)]
pub report: Account<'info, ScamReport>,
// one mark per family and wallet: the same family can't vote twice
#[account(init, payer = guardian, space = 8 + ReportMark::INIT_SPACE,
seeds = [b"reported", vault.key().as_ref(), address.as_ref()], bump)]
pub mark: Account<'info, ReportMark>,
pub system_program: Program<'info, System>,
}
And inside the instruction, the proof has to match:
require!(address != ctx.accounts.vault.owner && !ctx.accounts.vault.trusted.contains(&address), VaultError::BadSettings);
require_keys_eq!(proof.to, address, VaultError::NoProof);
require!(proof.status == Status::Rejected as u8, VaultError::NoProof);
The rest of the fairness rules: one vote per family, trusted contacts can never be reported, a report can be withdrawn, and the weight grows slowly. 1 family is a warning, 2 is strong, 3 families block. One report is a reason to call your family, not a verdict.
Measured, not promised
| A wallet without a guard | With Night Guard Wallet | |
|---|---|---|
| What Lucy sees before signing | program, accounts, an amount | β STOP and one sentence in her words, in 0.2 s |
| The 5 scam scenarios | each one signed with one tap | 5/5 stopped |
| The message that came with it | never read | 32/32 scam messages recognised, 0 false alarms on 28 family messages |
| After she taps "approve" | final | the vault refuses: 7 refusals on devnet |
| Cost to run | free: local Gemma, no API |
Fixed rules alone caught 22/32 scam messages with 3 false alarms. Gemma reading the story caught the 10 the rules missed. That gap is the reason the AI is there.
Partner Technologies
- Solana devnet + Anchor 0.31.1: the vault, the guardian flow, events and the community list. 12 instructions.
-
@solana/web3.js,@solana/spl-token, Token-2022 metadata, Solana Pay and Blinks: decoding, simulation diffs, fake-token detection, QR and link checks. - Gemma 4 E2B via Ollama: structured JSON, every quote checked against the message, on a CPU, no API, no cost per check. The message Lucy received never leaves the machine.
- GitHub Actions + GitHub Pages: build, deploy, the measured demo run, video and the interactive replay.
Hackathon Experience
I came in with a project I love, Night Guard, a local-AI guard for kids online, and one question: what would that idea look like on a blockchain, where nobody can call the bank and undo a payment?
Some real moments from the weekend:
My cloud machine was too old for Solana. The first build failed because the environment's system libraries were older than the Solana tools wanted. I switched to the official Anchor Docker image, and then my free cloud hours ran out mid-build. So the whole thing moved to GitHub Actions: build, deploy, the demo run, the video. My computer only opened the browser. Honestly, that ended up being the best decision of the weekend, because every number in the README comes from a run anyone can repeat.
Rust versions fought each other. A dependency had moved to a newer Rust edition than the Solana toolchain spoke. Pinning two crates and one resolver flag fixed it. Finding which two took longer than writing them.
The public RPC said "slow down". My demo run hit rate limits and crashed. I added patient retries and a small state cache, and the 42 checks finally finished in one go.
Gemma told Lucy she had received money. In Portuguese, in a scam where she was about to send 1.8 SOL. The amount was right, the tone was right, and the direction was backwards. That is the wrongWay line in the code above. It taught me that checking numbers is not enough: you also have to check which way the money goes.
My first video sounded Spanish. I had picked a voice that turned "Hi, I'm Jennifer" into something else entirely. Rerecorded with Ava, in English first, Portuguese as a plus.
MLH strips images from the README on the project page. My README had charts and screenshots, and the page showed a wall of text. So I built a one-page showcase with the poster and the big numbers, and made the interactive demo a real thing judges can click instead of a picture.
What I learned: on Solana, the most important line of code is the one that says no. I spent more time deciding what the program should refuse than what it should do, and that is exactly where the safety lives.
What It Can't Do Yet
- Devnet only. The demo signs with demo keypairs on a server. A real product would run the guard inside the wallet app, with keys on the user's device.
- The message evaluation is small and fictional: 60 messages I wrote. It shows the approach, not field accuracy.
- Gemma on a CPU takes about 5.5 s per message. The verdict doesn't wait for it, only the explanation does.
- The look-alike and phishing checks use built-in lists. Production needs a maintained registry of verified tokens and domains.
- The community list needs more families to matter. Three is a block in the demo. In the real world, the threshold and the appeal process deserve a proper design with people who have been scammed.
Thank You
To MLH and the Solana Foundation for a hackathon that welcomed someone writing their first Anchor program. To the people who maintain Anchor, the Solana docs and the devnet tools. To Google for releasing Gemma with open weights, and to Ollama for making it run on a normal laptop.
And to every family that ever got the "hi mom, new number" message. This one is for you.
Every person and message in this project is fictional. Music in the video: "Funkorama" by Kevin MacLeod (incompetech.com), CC BY 4.0.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.






