Audit

Security

What the site does with your wallet, what it will never do, and the threat model for contracts that are still unwritten. Written honestly: where protection exists we say how, where it does not we say so plainly.

What this site never does

It never asks for your seed phrase or private key. It never requests approve or setApprovalForAll on the collection. It never sends a transaction you did not click. It loads no third-party scripts. If any of that happens, you are not on our site.
What we doWhyRisk
Read your connected addressshow your poolspublic data
Read balance and tokenURIrender the NFTsview calls only
Ask for a plain signatureprove the address is yourssignatures cannot move funds
Open a pool (after deploy)write state on-chaina normal transaction you approve

Connection is injected-wallet only — MetaMask, Rabby, Brave and compatible. We deliberately skip WalletConnect and social-login SDKs: each one is third-party code running on a page with wallet access. Fewer dependencies, smaller attack surface, and a strict CSP stays possible.

The ownership signature

The message is written so a human can read it in the wallet and understand it is not a transaction.

<domain> wants you to sign in with your wallet.

This signature proves you control this address.
It does NOT approve any transaction, token transfer or spending allowance.

App: Rain Pools
Address: 0x…
Nonce: <one-time>
Issued At: <ISO time>
Expires At: <ISO time>
  • Domain on the first line. If you are signing something with a different domain, it is phishing.
  • One-time nonce. A signature cannot be reused for a new session.
  • 12-hour lifetime. Expired sessions are dropped automatically.
  • Verified by cryptography, not by trusting storage. On every visit the saved signature is re-checked with verifyMessage. Faking a browser record achieves nothing.
An honest limitation right now
The nonce is generated in the browser, because the site has no backend. That is enough to prove address ownership on this device, but not enough for server authorisation — a server cannot confirm it issued that nonce. The moment there is an API holding money or privileges, the nonce must come from the server and be burned after first use. Today the signature unlocks nothing but the interface, so there is no exposure — but this must be closed before the first server endpoint.

Frontend hardening

MeasureStatusDetail
Content-Security-Policydoneno external scripts or frames, frame-ancestors none
ClickjackingdoneX-Frame-Options: DENY
HSTSdoneforced HTTPS, two years, preload
Referrer-Policydoneyour address never leaks in a referrer
Permissions-Policydonecamera, mic, geolocation, payments all off
Analytics and trackersdonenone at all
External fonts and CDNsdonenone — the logo is SVG, fonts are system
Dependenciespartialminimal set; still needs regular npm auditing

Custody while staked

This is the most sensitive part of the project, so the decision favours safety over convenience: the NFT never leaves your wallet.

ThreatHow it is closed
Compromised staking contract drains the collectionImpossible — it holds no approval and cannot transfer. It only stores an “open” flag.
A bug locks NFTs foreverA hard ceiling on lock duration. Past it the lock stops applying, with or without the team.
Owner swaps staking for a malicious contractThe address is set once and becomes immutable.
Selling an NFT with an open pool breaks accountingTransfers revert at the collection level while the pool is open.
Phishing site asks for approval &ldquo;to stake&rdquo;The real site never needs approval. Any such request is fraud.

Contract threat model

VectorStatusMitigation
Reentrancy on claimbefore deploychecks-effects-interactions plus ReentrancyGuard
Validator manipulates weatherbefore deploycommit-reveal instead of block values
Silent keeper freezes the gamebefore deploypublic fallback reveal on timeout
Keeper picks a favourable seedbefore deployhash published before the event starts
Opening a pool on someone else's NFTbefore deployownerOf == msg.sender on every open
Exiting right before a droughtbefore deploy8-hour lock, early close reverts
Overflow in reward mathbefore deploySolidity 0.8 checks plus an emission cap
Unlimited reward mintingbefore deployhard cap on $RPOOL, vault is the only minter
Push distribution breaks on one recipientbefore deploypull model
Gas bomb in batch callsbefore deployarray length limits
Silent upgradebefore deploynon-upgradeable contracts, 48h timelock on parameters
What “before deploy” means
These are designed, not implemented — the contracts do not exist yet. Nothing here should be read as audited until the code is written, tested and reviewed by someone else.

Checklist before mainnet

  • Full test coverage: open, close, early-close attempts, opening someone else's token, silent-keeper behaviour.
  • Fuzz the reward math over long event chains — accruals must never go negative or overflow.
  • Static analysis (Slither, Aderyn) with every warning triaged.
  • Full testnet run: mint, open, a streak of rain and drought, claim, close, resale.
  • External review by someone who did not write the code.
  • Source verification in the explorer before the first public sale.
  • Server-issued, single-use nonce once an API exists.
  • A funded bug bounty before rewards go live.

Rules for pool owners

  • Check the domain before connecting. The first line of the signature must match the address bar.
  • Rain Pools will never ask for a seed phrase, a private key, or setApprovalForAll. Any such request is phishing, however convincing the page looks.
  • Read the whole signature text. A genuine login says in plain words that it is not a transaction.
  • Keep valuable NFTs on a separate wallet and your main funds on hardware.
  • While a pool is locked you cannot sell it. That is expected behaviour, not a failure.