Live app: https://noryx-lyart.vercel.app Repo: github.com/angelraph/NORYX
For judges in a hurry: you don't need a wallet to try this. Paste any address (yours, or a well-known one) into the lookup box on the live app and you'll see a real, live scan — no connection, no gas, no signup.
Contracts (Monad Mainnet, all Sourcify-verified):
SecurityProfile:0x884EEa8281C15c3516f10Cc6864EBBaA453AF9d8ScoreRegistry:0x61b7de677d6548df8df1f73e107b69d67eee606bScoreGatedDemo:0xa9ead41e9b1a5e2e1b388a5a6e4c7d450f9f73ab
"Every wallet drainer story starts the same way: someone approved a contract they shouldn't have, and never checked back. Noryx scans your live token approvals on Monad, scores your wallet's risk, and lets you revoke what's dangerous in one click — all against real on-chain data, nothing cached or mocked. But we didn't stop at a dashboard: your security score can be published on-chain as an attestation, so it's not just something our UI tells you — it's a fact any other Monad contract can check. We even shipped a second, independent contract that proves it: it only lets a wallet through if it has a published Noryx score above a threshold."
- Approving a token contract is the #1 mechanism behind wallet-drainer exploits — not phishing links, not private key theft, an approval you clicked and forgot about.
- There's no easy way to see, across everything you've ever approved, which approvals are still live, how large they are, and whether the spender contract itself is trustworthy.
- Existing tools that solve this (revoke.cash and similar) stop at showing you a list and a client-side score. That score never leaves the browser tab — it's not something anything else can check.
No connection required to look — paste any address and Noryx scans it read-only. Four things happen automatically the moment there's an address to look at (yours, connected, or anyone else's, pasted in):
- Scan — every
Approvalevent emitted recently, re-verified against the current liveallowance()(a logged approval may already be spent or replaced). - Audit — every spender contract gets checked for Sourcify
verification, deployment age (binary-searched from historical
eth_getCode, not guessed), and whether it's even a contract. - Score — a 0–100 Wallet Security Score from approval size + the contract-level findings.
- Fix — one-click real
approve(spender, 0)revoke transactions.
Plus a saved on-chain policy (SecurityProfile): the user sets their own
rules (e.g. "flag unlimited approvals") and every scan checks against
their own saved preferences, not a generic threshold.
Someone will say "revoke.cash already does this." The honest answer: scanning + revoking is table stakes, several tools do it well — Noryx also matches revoke.cash's no-connection viewing (paste any address, see its scan, no wallet required). The structural difference is what happens to the score:
| revoke.cash / typical scanner | Noryx | |
|---|---|---|
| Score lives | client-side only, gone when you close the tab | published on-chain as a timestamped attestation |
| Who can check it | only that tool's own UI | any Monad contract |
| Proof this matters | none — it's a claim | a second, independently deployed contract (ScoreGatedDemo) that actually reads the attestation and gates an action on it |
This turns Noryx from "a dashboard" into a small piece of on-chain
security infrastructure — and it's only economical because Monad's
writes are cheap enough to publish a fresh attestation on every scan.
This is not a data-availability trick; the ScoreRegistry attestation is
enforced with real require() checks on-chain, verifiable independent
of Noryx's frontend.
A different objection: "Blockaid / Wallet Guard / Rabby already warn me before I sign anything." Those tools try to explain every transaction type generically — broad but shallow. Noryx narrows to the single highest-value, best-understood attack surface, token approvals (the actual mechanism behind most wallet-drainer stories), and does that one thing with real on-chain data end to end, rather than a shallow pass across everything.
Have two wallets ready ahead of time: Wallet A with some real approval history (so the scan/revoke/score parts have something to show), and Wallet B with a clean history (score ≥ 70) so the gated demo can show a pass, not just a revert. Confirmed working end-to-end on 2026-07-18.
- Connect Wallet A. Narrate while it scans: "This is reading live
Approvalevents and re-checking each one against the current allowance right now — nothing here is precomputed." - Point at the Wallet Security Score card. Call out one flagged approval (unlimited, or an unverified/newly-deployed spender) and explain the specific reason it's flagged.
- Click Revoke on that approval. Wait for confirmation. "That's a
real
approve(spender, 0)transaction, just landed on Monad Mainnet." - Scroll to On-Chain Score Attestation. "Right now this score only lives in my browser. Watch what happens when I publish it." Click Publish score on-chain, confirm, wait for the tx.
- Click Try gated action. With Wallet A likely below the demo threshold (70), this should revert — call that out explicitly: "This is a contract I don't control, actually rejecting me because my published score isn't high enough. That's the proof this is real enforcement, not a UI badge."
- Switch to Wallet B. Publish its (higher) score, click Try gated action again — this time it succeeds. "Same mechanism, same contract, different outcome, purely because the on-chain attestation is different. That's composability: any Monad project could gate a mint, an allowlist, a lending term, on a wallet's Noryx score, without ever talking to us."
A short standalone clip for the read-only lookup, separate from the main demo above — good as a second post or a reply with more depth. Record in a private/incognito window so nothing auto-connects. Confirmed working end-to-end on 2026-07-19.
- Load the app with no wallet connected. "No wallet, no connection — watch this."
- Paste in any address, hit View. "That's a live scan of a wallet I don't even own — real approvals, real balance, straight off Monad Mainnet. No gas, no signup."
- Point at a disabled action (Revoke shows "Read-only", or the score card's "Connect this wallet to publish its score"). "Read-only where it should be — I can look at anything, but I can't act unless it's actually my wallet signing."
- "Isn't this just an oracle for your own number?" — Yes, and that's the point: it's a user-controlled, verifiable oracle. The user signs the publish transaction themselves; nothing is pushed on their behalf. Anyone can independently recompute the score from the same on-chain data Noryx reads (approvals, verification status, contract age) and compare it to what was published — it's not a black box.
- "Why not gate real value (a vault, a mint) instead of a toy
contract?" — Deliberate scope cut for the hackathon timeline; the
toy contract isolates and proves the composability mechanism (a
require()againstgetScore()) without adding financial risk to a live mainnet demo. The mechanism is identical to what a real integration would use. - "What stops someone from publishing a fake high score?" —
publishScoreisn't currently validated against Noryx's own computation on-chain (the scoring logic lives in the frontend, per theSecurityProfiledesign). Today the trust model is "the number came from Noryx's published, inspectable scoring logic, signed by the user's own key" — same trust model as any client-computed value a user chooses to attest to. Flagged as the natural next step: move scoring on-chain or add a verifier. - "Does this work on any chain?" — Contracts are Monad Mainnet only right now (chain 143). Nothing in the design is Monad-specific beyond relying on cheap writes to make per-scan publishing economical.
- Next.js 16 (App Router) + TypeScript + Tailwind CSS
- wagmi 3 + viem for all on-chain reads/writes
- Solidity (
SecurityProfile.sol,ScoreRegistry.sol,ScoreGatedDemo.sol), compiled/deployed/verified with plain Node scripts (no Foundry/Hardhat) - Monad Mainnet (chain ID 143)
See README.md for full technical detail on the approval-scan
mechanics, contract-risk checks, and rate-limit handling.
Testnet assumptions don't carry over to mainnet even when the RPC
interface looks identical. I had to empirically re-measure Monad
Mainnet's real block time (~0.4s, not testnet's ~1s) and its
eth_getCode historical-state retention window (~1,928,000 blocks, not
testnet's ~5M — see How the contract-risk checks
work), then recalibrate
every block-count constant in the scan logic so scan windows and
"deployed X ago" labels stayed accurate on the new chain instead of
silently drifting.
Also found Monad's own brand kit inconsistent between its documented
swatch and its live logo's actual fill color — worth verifying brand
assets the same way you'd verify anything else (see bee907a /
807f288).