Documentation
Every tool, what it costs on-chain, which networks it runs on, and where the limits are. Written from the code that runs the site — including the parts that are not finished, because a manual that only lists strengths is an advert.
Nothing in the docs matches that. Try a tool name, a chain, or a word like fee, credits or escrow.
A security company that grew a toolbox. The audit business came first — smart-contract audits since 2021 — and the tools exist because the same questions kept coming up: is this token safe, can I lock the liquidity, how do I pay a thousand wallets, how do I take payment in crypto without a processor.
There are 12 tools, all reachable from the Tools menu on every page. Nine of them touch the chain; three are read-only. None of them takes custody of anything.
You sign every transaction, in your own wallet, and you own whatever comes out. When you deploy a locker, an escrow or a storefront through this site, the contract is deployed by your wallet and owned by your wallet. SaferICO cannot pause it, upgrade it, or move what is inside it. The trade-off is real and worth stating: if you lose your keys, we cannot help you recover anything.
The full detail is in the privacy policy.
Every page has a Connect wallet button in the top right. It asks your wallet for your address and nothing else — connecting is not a transaction, costs nothing, and grants no spending permission.
The scanner, the market terminal and the portfolio tracker all work on a pasted address — none of them ever asks you to sign a transaction. A wallet is still how you sign in to a plan, and how a per-scan fee is paid if you do not hold one.
A plan is tied to a wallet address. Signing in asks you to sign a plain text message — free, off-chain, and not a transaction. That signature proves you hold the wallet; it cannot move funds.
25 EVM mainnets. Every one of them cleared two checks on the day it was added, and both were measured rather than read from a chain list: two or more public RPC endpoints answered with the right chain id and a block seconds old, and the chain is served by the same explorer API key we already hold — so verified source can actually be fetched there. A chain with a live RPC but no explorer API gives you half a scan, and half a scan reported as a scan is worse than no scan.
Reading a chain and building on one are different capabilities. Every network below can be scanned, monitored and badged. The build tools (token, NFT, lock, distribute, market) are priced on the seven marked below, and a plan is paid on six of those — Avalanche can deploy but is not a payment network.
Manual audits are not limited to this table at all: a person reading Solidity needs no integration, so an audit can be done on any EVM chain.
| Network | Chain ID | Coin | Build & deploy | Market data |
|---|---|---|---|---|
| Ethereum | 1 | ETH | yes | yes |
| BNB Chain | 56 | BNB | yes | yes |
| Polygon | 137 | POL | yes | yes |
| Base | 8453 | ETH | yes | yes |
| Arbitrum | 42161 | ETH | yes | yes |
| Optimism | 10 | ETH | yes | yes |
| Avalanche | 43114 | AVAX | yes | yes |
| Abstract | 2741 | ETH | — | — |
| ApeChain | 33139 | APE | — | — |
| Berachain | 80094 | BERA | — | — |
| Blast | 81457 | ETH | — | — |
| Celo | 42220 | CELO | — | — |
| Fraxtal | 252 | frxETH | — | — |
| Gnosis | 100 | XDAI | — | — |
| HyperEVM | 999 | HYPE | — | — |
| Katana | 747474 | ETH | — | — |
| Linea | 59144 | ETH | — | — |
| Mantle | 5000 | MNT | — | — |
| Monad | 143 | MON | — | — |
| opBNB | 204 | BNB | — | — |
| Sei | 1329 | SEI | — | — |
| Sonic | 146 | S | — | — |
| Taiko | 167000 | ETH | — | — |
| Unichain | 130 | ETH | — | — |
| World Chain | 480 | ETH | — | — |
Amoy (80002), Arbitrum Sepolia (421614), Avalanche Fuji (43113), Base Sepolia (84532), BSC Testnet (97), Hoodi (560048), OP Sepolia (11155420), Sepolia (11155111). Deploy fees on testnets are token amounts, so practice runs cost almost nothing. Only the five the faucet funds can be topped up here; the rest are readable but you bring your own gas.
Holesky (17000) is retired. It stopped producing blocks and is kept only so old links and saved scans still resolve — nothing new should be deployed there. Hoodi replaced it.
A limit worth knowing: live price, chart and pool data comes from public DEX aggregators, and they index far fewer chains than we can read. The Market data column above says exactly where that half works; on every other network the security scan is complete and the price panel is empty. That is the data source, not a fault in the page.
Paste a contract address, get a risk report. The scanner reads the contract's own source and bytecode — not its price, not its volume, not its social media.
Auto Scan is a machine reading patterns; it is fast, free to try, and produces a PDF. A manual audit is a person reading your contract line by line and writing findings you can act on. They answer different questions. An automated pass cannot tell you that your business logic is wrong — only that your code does not contain known-dangerous patterns.
Say this out loud before you trade on it: a clean scan means no known pattern fired. It is evidence, not a guarantee, and it is a point-in-time reading of a contract that may be upgradeable.
Every time you use a DEX, a mint or a bridge, you grant that contract permission to move a token on your behalf. Those permissions do not expire. Years-old approvals to contracts that have since been exploited are one of the most common ways wallets are drained long after the owner stopped using the app.
Unlimited approvals are the ones to hunt first. They are the default in most apps and they mean "this contract may move all of this token, forever."
A live <img> for your site, README or docs. Generate it at /scan#badge.
GET /api/badge/<chainId>/<address>.svg — the small badge.?style=card for the larger card.GET /api/badge/data?chain=&address= — the same facts as JSON.No key, no signup, CORS open — it is an image on somebody else's page. Responses are cached for 15 minutes at the edge, so a badge on a busy page costs one cached read rather than a scan.
The badge deliberately reports two different kinds of thing and the verification page keeps them apart:
eth_getCode), source published on the explorer, EIP-1967 proxy slot, and whether owner() answers the zero address. Read on request, every time.A fact we could not check renders as “unknown”, never as “no”. An RPC timeout must not put “ownership not renounced” on somebody's site over our name.
The snippet wraps the image in a link to /scan#badge=<chain>:<address> — the same screen the badge was generated on, showing the full record for that contract. What the embedder sees when they create it is exactly what a sceptic sees when they click it.
Badges are offered on the seven mainnets only. A “scanned” badge for a testnet deployment is a claim about code that is not the code handling anyone's money.
203 documented ways a Solidity contract loses money, one page each, at /safe/. This is not a separate reading list — it is the same table the scanner grades against. When a scan reports SAFE-0107, the id in the report links straight to the entry that explains it.
Severity uses the same four-level scale as a scan report and the Telegram bot — critical, high, medium, low — so a class graded "high" here is the same red you see in your own results. Every entry also carries a CWE id, and an SWC id when one exists: 68 of the 203 have none, because the SWC registry stopped being updated and ends at SWC-136. Vault inflation, L2 sequencer-uptime feeds, ERC-4626 rounding and EIP-7702 delegation are all in that gap. Those entries say "No SWC entry" rather than linking a page that is not there.
The index at /safe/ filters by free text, by severity and by category, and the filter is written into the URL — so /safe/?c=Reentrancy or /safe/?s=critical is a shareable view. The full list is real HTML, so it also works with JavaScript off.
201 of the 203 have an automated detector behind them; the remaining two are documented for manual review and say so on the page.
The 20 largest smart-contract failures on record — $5.67B between them, 2016 to 2026 — one page each at /hacks/.
The index filters by vulnerability type, by year and by chain, and carries the filter in the URL the same way /safe/ does. One incident (Curve, 2023) is tagged as two classes at once — a compiler bug and a reentrancy — and appears under both.
These pages are generated from the research already published in the FastBNB case study, which remains the single-page version.
Every audit SaferICO has published — 204 projects, 222 PDF reports, 2021 to 2025 — indexed at /audits/, one page per project, each linking the original report on GitHub.
This is the published archive, not the whole of the work. A report is only published with the client's explicit approval; until a client consents to public disclosure their report stays private. So the count here is what can be shown and verified — anything that cannot be opened and read is not counted.
The pages are generated, never hand-written. scripts/audits-harvest.mjs reads the public repositories at github.com/Saferico, opens every PDF inside them and lifts the fields the report itself prints; scripts/audits.mjs lays those out. npm run audits -- --check runs before every deploy and stops it if a page has drifted from the data.
Where a report prints an address on a chain we hold an RPC for, eth_getCode is run against it when the pages are built, and the result is shown on that audit's page. It describes what became of the project, not the quality of the audit. Many contracts here were reviewed before launch and never deployed, and most of the archive predates 2023 — so no page turns this into a percentage or a headline, because a survival rate presented next to an audit reads as a grade for it.
npm run audits:harvest && npm run audits after publishing a new audit repository. The harvest is free: repository contents come from codeload.github.com, which does not draw on GitHub's 60-per-hour unauthenticated API budget, so no token is needed. A harvest that returns fewer audits than the last one refuses to write — a failed fetch and a deleted repository look identical in the output, and only one of them should shrink the archive.
A single 0–100 reading of the permissions your wallet currently has outstanding, at /wallet-security#score. Approvals, not balances, are what a drain actually uses.
From the approval scan on the first tab — never from a guess. A wallet nobody has scanned gets no score at all, because "no approvals found" and "nobody looked" produce the same empty list and must not produce the same number. Each distinct spender is then checked for contract code, published source and whether it is an upgradeable proxy.
Every point taken off is named on screen with the reason:
85+ is Well secured, 65+ Secured, 40+ Poorly secured, below that Insecure — the same four tiers a contract scan uses.
What it cannot see: a compromised key, a malicious signature you already gave, or approvals on a chain you have not scanned. Approvals are per-chain; run it on each chain you use. A spender the lookup could not reach is scored as an ordinary approval, never as a clean one, and the page says how many those were.
Paste a token at /wallet-security#reputation and it finds the wallet that deployed it, then reports what else that wallet has shipped.
It will not tell you anyone rugged anything. A rug is an intent, read after the fact from price action, social accounts and a community's own account of what happened — none of which is on-chain. Guess wrong once in public about a named developer and the tool is finished. So this reports counts an explorer will confirm if you click through, and leaves the judgement where it belongs. A brand-new wallet with one unverified contract is also how every honest launch starts.
Counts are a floor, not a total: the first 200 transactions are read, and the page says so when there are more.
Four questions before you claim anything, at /wallet-security#airdrop, in the order they bite.
approve, increaseAllowance, setApprovalForAll, transferFrom, permit, Permit2 — and an unlimited amount is called unlimited, in capitals, next to the address it would be granted to.A field you leave empty is reported as unchecked, never counted as a pass. An unrecognised selector is reported as unrecognised and rated worth stopping for — not waved through.
The strongest defence remains the boring one: reach a claim page only through a link the project itself published.
Add @SaferICOAuditBot to a group and it answers the questions a chat cannot answer for itself.
Deleting a message in somebody else's group is irreversible and wrong in every false positive, so deletion is off until an admin runs /guard delete on — and even then it only ever applies to links, never to an address (not an offence) and never to a person (a name collision is not proof).
/setprivacy → the bot → Disable, then remove and re-add it. /guard says so in the chat rather than reporting a green tick over a check that is structurally blind.Commands (group admins only): /guard · /guard on|off · /guard links on|off · /guard names on|off · /guard delete on|off. Replies are rate-limited per chat and the same address is not answered twice within an hour.
There is no scam-domain blocklist, deliberately: drainers rotate domains hourly, and a stale list gives a group the false comfort of a green light. What does not rotate is the structure of the attack.
Five security games on one season leaderboard. It is a real product surface, not a toy: the scoring runs on the server and the on-chain missions are graded by reading the chain.
100 / 200 / 300 base points by difficulty, up to +50% for speed, and up to +50% for a day streak. A partly-right answer scores 40%. Every challenge counts once per account, ever — the same rule that stops a payment being credited twice.
The speed bonus has a reading floor of half a second per line. Nobody reads ten lines of Solidity in 0.4 seconds, so an answer faster than the floor is scored as if it arrived at the floor. That is not an accusation — it is the ceiling on what the fastest honest read can earn, and it exists because signed-out practice shows you the answer afterwards.
Signed out you get the same challenges and the same scoring, but nothing is stored and nothing reaches the board. Signed in (wallet or GitHub, both free) your season starts counting. A plan raises the daily cap — 6 challenges a day free, 15 on Starter, 40 on Pro, 80 on Business, 200 on AI Agents — and unlocks the duel. It does not buy points.
On the Rug or Real cases: they are reconstructed patterns. No address, ticker or project name appears in them, on the page or on the server. Naming a live contract a scam is a claim for a courtroom; teaching the shape money leaves through is a lesson.
Four read-only tools over live pool data: a chart terminal, a feed of newly created pools, a local watchlist and a wallet portfolio tracker.
Search by symbol, name, or paste a contract address. Prices come from the deepest live pool for that token, because the same token exists in a dozen pools and only the deep one has a real price. Every row is one click from a full security scan.
Pairs created in the last hours, newest first. This is where launches appear — and where most rug pulls live. A pool minutes old has no history, its liquidity may be unlocked, and the deployer can usually still mint or blacklist.
Stored in your own browser under sfi_watchlist_v1. No account, nothing uploaded, and it can be exported to JSON and imported again.
Connect or paste an address to see holdings priced at live pool rates, with a risk flag on each token.
How it finds your tokens, and what it misses: there is no free "list every token in a wallet" endpoint, so the tracker reads the wallet's transfer history from the public explorer to learn which tokens it has touched, then reads each live balance on-chain. Tokens that never appeared in a transfer log, and airdropped spam with no pool, will not be priced.
Swap a token, or bridge across chains, at the best route from an aggregator that quotes 30+ DEXs and bridges. SaferICO takes no cut of a swap. You pay network gas and whatever the route's own protocols charge, both shown before you sign.
A cross-chain transfer confirms on the source chain first, then arrives on the destination minutes later. The transfer stays under My Transfers and the page polls the bridge until it lands. Closing the tab does not cancel it — the transfer is on-chain either way.
The page shows the destination token's pool depth and 24h volume, and links straight to a scan. Thin liquidity means a large buy moves the price hard and you may not be able to sell out.
Create a token, launch a staking pool, or burn supply — without writing Solidity. The page ships the compiled bytecode and your wallet deploys it, so the contract is yours from the first block. There is no SaferICO address in the middle.
Choose the name, symbol, supply and decimals, then the optional features. Each paid feature adds to the deploy fee — see fees. Features you do not enable are not in the deployed code at all, which is the point: a token with no mint function cannot be inflated, and a scanner can see that.
Burning sends tokens to a dead address, permanently. It is irreversible and the page will say so before you sign. You get a certificate page with the transaction hash so the burn can be verified by anyone.
Lock liquidity or team tokens until a date, or release them gradually on a vesting schedule with an optional cliff. Both produce a public proof anyone can check — which is the entire value: a lock nobody can verify proves nothing.
Choose the token, the amount and the unlock time. Tokens move into the locker contract and cannot be withdrawn before the unlock time — not by you, not by us. You can extend a lock (later is always allowed) but never shorten one.
Set a start, a duration, and optionally a cliff before anything unlocks. The beneficiary claims what has vested; nothing can be pulled forward. A schedule can be created as revocable, and that flag is visible on-chain — so a "revocable" schedule is not a promise anyone has to take on trust.
Current status: the locker and vesting contracts are written, compiled and tested, but the per-network deployments are not live yet, so this page cannot be used on mainnet today. Everything else on the site self-deploys and is unaffected.
Two ways to get tokens to many wallets.
You pay the gas and everyone receives in one flow. Right for a few hundred recipients, and for payouts where you want the tokens to arrive without the recipient doing anything.
You upload the recipient list, the page builds a Merkle tree and deploys a claim contract you own. Recipients claim their own allocation and pay their own gas. Right for thousands of wallets — the cost does not grow with the list.
The snapshot tool in Dev & Market can produce a holder list and hand it straight to this page. It fills in the addresses and deliberately leaves the amounts blank: a snapshot is what each wallet holds, not what you owe it.
Generates a complete project website as a single index.html — live price, chart, buy button, tokenomics, roadmap and audit badges. It has no build step and no runtime dependency on saferico.com: the generated page fetches its own market data directly from the same public APIs this site uses.
Everything is generated in your browser. Nothing is uploaded. Download the file and host it anywhere that serves static files.
Worth saying plainly: a good-looking site takes ten minutes now, which is exactly why investors have stopped treating one as evidence. What moves people is a locked pool, a renounced or timelocked owner, and an audit they can read.
Generates a complete, runnable Telegram project from one of thirty templates — twenty Mini Apps and ten bots: the server, the web app where there is one, the deploy files and a README. You fill in a form; you get a .zip. Browsing the templates and filling the form is open to everyone — generating the project needs the AI Agents plan.
balanceOf.curl.A bot is a different shape of project, not a Mini App with the interface switched off: no app/ folder, no web page to host, no initData to verify and no HTTP API. Its entire public surface is the Telegram webhook, and that refuses any request without the secret token Telegram sends in a header.
/close can report a real percentage and the record counts only closed trades.The Mini App itself is identical in both; only the server differs. The Node projects use built-in fetch and crypto.subtle; the Python projects use only the standard library. npm install installs nothing, so nothing can break it — and the same Node source runs unchanged on a VPS, in Docker and on Cloudflare Workers, because the storage driver picks itself from the environment.
Not a template feature — part of the shared core, so all twenty have it and none of them had to be asked. The account in ADMIN_ID sees a gear in the app header; behind it are the user count, how many were active today, a message box that reaches everyone, a searchable list of every user with a Ban button, a CSV export, and a log of every admin action taken. The same things work typed at the bot: /admin, /users, /broadcast, /export, /ban, /unban.
Those commands are registered for the owner's account only, so they never appear in the menu every user sees, and typing them from any other account does nothing at all. A broadcast goes out in slices of about 25 a second — Telegram rate-limits bots, and a plain loop over a few thousand people fails halfway — and it is resumable, so an interrupted send continues rather than starting over and messaging people twice.
The user list is written by the server on every verified request, which is what makes any of this possible: nothing else in a Mini App knows its own audience. A ban is enforced before the template's own code runs, and an admin cannot ban themselves out of their own app.
Every /api/* route in a generated project validates Telegram's initData before any handler runs: an HMAC-SHA256 derived from your bot token, compared in constant time, with an age limit so a captured string is not a permanent credential. Handlers receive a verified user; they never read a user id out of the request body. That check lives in one file — bot/auth.js or bot/auth.py — and no template can skip it.
The generated app also ships a Content-Security-Policy: it may load no script but Telegram's own SDK and may talk to nothing but its own API, so an injected tag cannot phone anywhere. Two identical writes at once — the double tap on Pay that used to become two orders — are refused by the server, not just by the button.
Our own test suite generates every template and fails the build if a secret ever appears anywhere under app/. It also signs a real initData and runs the generated auth.js and auth.py against it, checking that a tampered hash, a different bot token, a swapped user and a stale timestamp are each rejected.
Nowhere. The fields marked secret — your bot token, your AI API key — are stripped from the request before your browser sends it. The server builds the project without them and writes a blank .env.example; your browser then writes the real .env locally and packs the zip. The only copy of your token that exists outside the form is the one inside the file you downloaded, and .env is git-ignored.
Your other answers — colours, product names, questions, prices — do reach the server, because they are compiled into the code it generates. Nothing is stored.
The generation itself runs on our Worker rather than in your browser, and that is deliberate: the thirty templates are the product. Code shipped to a browser is a free download for anyone who opens developer tools, subscription or not.
The project is yours: run it, change it, sell what it does, keep it after your subscription ends. Nothing in a generated project expires, checks a key or calls home — a project that could stop working would not be worth generating, and there is no licence server to go down.
The licence asks one thing: do not re-sell the templates themselves as material for other people to build from — a template pack, a competing builder, a public repo of “free Telegram templates”. Publishing your own finished app is fine; that is your product, not ours. The terms ship as LICENSE.md in every zip, in plain English.
Every download carries a build stamp — an opaque id derived from your account, never your wallet address itself — so a leaked copy can be traced back to where it came from. There is also a daily cap on how many projects one account may generate, set far above what building real projects takes and far below what copying the catalogue would take. Neither changes a line of the code you receive.
Each template declares the variables it reads and the hosts it can deploy to. The README is built from those declarations, so it cannot document a key the code does not use, omit one it does, or tell a Python project to run wrangler.
Twelve extension templates. Pick one, fill in a form, choose a browser, and download a complete Manifest V3 project — not a starter, a finished extension you can load and use the same minute.
Every template builds for Chrome (which covers Edge, Brave and Opera), Firefox and Safari. These are not the same file with a different name: Firefox needs a background script where Chrome needs a service worker, and a Firefox add-on loaded temporarily gets a new id on every reload — and with it a new, empty storage area, so your test settings vanish each time you reload. The build ships the explicit add-on id that fixes that, per target, rather than leaving you to work it out.
A working tree, not a scaffold: manifest.json, background.js, popup/, options/, content/, a shared lib/ (storage, theming, UI), a README.md with the load-and-test steps for your browser, and store/listing.md — a first draft of the store listing text, which is the part everyone forgets until submission day.
The name field stops at 45 characters and the one-line description at 132, because those are the limits the Chrome Web Store enforces at submission — after you have written the listing, taken the screenshots and paid the fee. Versions are numbers only, for the same reason: 1.0.0-beta is rejected at load. Being told now costs a second; being told then costs an afternoon.
Unlike the Mini App Builder, nothing sensitive travels here in either direction. An extension has no server and no .env: any API key belongs to the person who eventually installs it and is typed into that extension's own settings page. So there is nothing for us to strip and nothing that could end up in a log by accident.
You get the code; the listing is your account. Chrome charges a one-off developer fee and reviews new extensions, Firefox reviews too, and Safari requires Xcode to wrap the extension into an app. You can also load the folder unpacked and use it privately forever without any of that.
Part of the $59.9 AI Agents plan. Up to 40 projects a day, shared with the Mini App Builder — a subscription buys you projects, not a copy of the catalogue. The generators run on our server and are never sent to the browser; you receive the finished files. The gallery and every form field stay public so you can see exactly what you would be buying before you buy it.
The toolbelt: the small things you need mid-build and would otherwise go looking for on four different sites.
Reads a token's holders at the current block and produces an address list. Balances are read in batches to stay inside public RPC limits. The result hands off straight to Distribution — with the amounts deliberately left blank, because a snapshot is what each wallet holds, not what you have decided to send it.
Sign a plain text message with your wallet, or verify someone else's signature. Useful for proving control of an address without a transaction. Signing a message is free and moves nothing.
Current gas across the supported chains and live native-coin prices, from the same sources the rest of the site uses.
Two different things share one wallet, and the difference explains every limit on the page.
Tell the faucet what you are about to deploy. It prices that action against the network's live gas price, adds 25% for a busy block, caps the result, and sends exactly that much to the wallet your plan is on. Up to six a day per network.
It is a top-up, not a relayer, and that is deliberate: every contract in this suite sets its owner to whoever sent the deployment, so a contract deployed by our wallet would belong to our wallet. Sending you the fee keeps you as the deployer and the owner.
If you already hold part of what the deploy costs you get the difference, not another full grant. If you already hold enough, nothing is sent and the page says so.
A flat amount of the network's own coin, straight to your wallet, no justification asked. Once per network per day, up to four a month. The allowance does not roll over — an unused month is gone rather than banked.
BSC Testnet (tBNB), Sepolia, Hoodi, Polygon Amoy and Base Sepolia. Test networks only: the faucet refuses any chain that is not flagged as a testnet in two independent lists that have to agree, so it cannot touch a live network even if asked to.
Testnet coin cannot be minted, bought at scale or recovered — official faucets ration what anyone can collect. So each network has two floors. Cross the first and withdrawals pause while sponsored deploys keep working; the bonus gives way to the promise, never the other way round. The page always shows the real state of every network and will never offer a claim it cannot pay.
The wallet that owns the plan, and only that — there is no destination field anywhere in the interface or the API. The faucet wallet is 0x48AD8E5bb703E1d4Da68d419A4735f4963763eb1; it holds testnet coin and nothing else, and every drip is a public transaction you can check.
One warning. Testnet coin is never sold. Anyone offering to sell you tBNB or Sepolia ETH is usually sending an ERC-20 token that merely shares the name — and a token cannot pay gas. Real native coin appears in your wallet's main balance, never in the token list.
Deploy your own storefront contract and sell NFTs from it. There is no global marketplace here and that is deliberate — nobody at SaferICO decides whose collection is listed.
Listing does not move your NFT. You approve the storefront, and the listing records that approval. The NFT stays in your wallet until someone buys it. At fill time the contract re-checks ownership and approval, so a stale listing reverts instead of taking a buyer's money for something you no longer hold.
Opening a shop puts it in the address bar: saferico.com/marketplace?store=0x…&chain=56. Share that link and anyone who opens it lands in your shop. Anyone can list on any storefront, so the shop is owned by whoever uses it.
You set a storefront fee up to 10%, fixed at deploy time. SaferICO takes 1% of each sale, also fixed in the contract at deploy time and immutable afterwards — the storefront owner cannot raise it and neither can we.
One contract per deal, for trading with someone you do not trust. Funds sit in the contract, not with either party and not with us.
| Exit | Who | When |
|---|---|---|
| Release | Buyer | Goods received — funds go to the seller |
| Refund | Seller | Seller returns the money voluntarily |
| Resolve | Arbiter | After a dispute, the arbiter awards either side |
| Claim expired | Buyer | Deadline passed with no release — money comes back |
An arbiter is optional. If you name one, either party can raise a dispute and the arbiter decides. A dispute cannot be outwaited: once a deal is disputed the buyer's expiry claim is locked for 30 days past the deadline, so the arbiter has a real chance to rule. After that window the claim reopens, because funds must never be strandable if an arbiter disappears.
With no arbiter you get a two-party deal with a deadline: the buyer releases, or the deadline passes and the buyer takes the money back.
Funding with an ERC-20 that takes a fee on transfer will deliver less than the deal amount. The contract rejects that rather than recording a deal it cannot honour.
Move holders from an old token to a new one at a fixed ratio — a V1 → V2 migration, run by a contract you own.
The ratio is computed in raw units and accounts for a decimals difference between the two tokens. The payout figure shown on screen is read from the contract's own quote(), not recomputed in the browser, so what you see is what the contract will pay.
You can pause a migration, and recover the new token — but recovery is locked until the deadline so a project cannot fund a migration, take the deposits and pull the payout tokens back out.
Accept crypto without a processor. You create a payment link or QR code, the customer pays straight into your wallet, and the page watches the chain until the money lands. No middleman holds the funds, no signup for the payer, and no percentage — there is nothing in between to take one.
The invoice is encoded into the URL fragment — the part after #, which browsers never send to a server. The link contains the whole request and nothing about it is stored by us.
Two QR modes: an EIP-681 wallet URI that a crypto wallet reads directly and pre-fills with address and amount, and a plain https link for a phone camera.
For an ERC-20, the page reads Transfer event logs to your address. For a native coin it reads the recipient's recent transactions from the public explorer. A transaction that has already settled another invoice cannot settle a second one.
An agent that reads chains, audits contracts and prepares transactions from a chat. It runs server-side with a real tool runtime, not a chat window that describes what it would do.
Every proposed transaction goes through a review sheet before it reaches your wallet: what it does, what it costs, what it touches.
The workspace on the web, and inside the Telegram Mini App. The same account and plan cover both.
Agent use is metered in credits — see plans. A chat turn costs 1, a server-side read costs 2, building and simulating a transaction costs 3, and a stored full audit costs 5.
A plan is tied to a wallet address, not an email. You pay from the wallet, and that wallet is the account. Signing in afterwards is a free signature.
| Plan | Price | Agent credits | What it adds |
|---|---|---|---|
| Free | $0 / 30 days | — | Unlimited scanning |
| Pro | $9.99 / 30 days | 1,500 | Auto-scan, SAFI standard, saved reports |
| Business | $29.9 / 30 days | 4,000 | SAFI advanced, the full tool hub, API access |
| AI Agents | $59.9 / 30 days | 12,000 | Everything, highest limits |
| Action | Credits |
|---|---|
| One SAFI chat turn | 1 |
| A server-side read tool | 2 |
| Building + simulating a transaction | 3 |
| A stored full audit report | 5 |
Plans are 30 days and do not auto-renew. There is no stored card, because there is no card.
The same tiers through the bot cost 50% less, payable in crypto or Telegram Stars.
Separate from a plan. A plan buys scanning and agent capacity; an on-chain fee is a one-off payment attached to a deployment, denominated in the network's own coin because that is what a transaction can carry.
You pay it once, when you deploy. There is no recurring charge on a contract you own and no fee on using it afterwards.
| Network | Locker / vesting / distribution | Escrow | Storefront / migrator |
|---|---|---|---|
| Ethereum | 0.048 ETH | 0.006 ETH | 0.024 ETH |
| BNB Chain | 0.16 BNB | 0.021 BNB | 0.08 BNB |
| Polygon | 1300 POL | 170 POL | 650 POL |
| Base | 0.048 ETH | 0.006 ETH | 0.024 ETH |
| Arbitrum | 0.048 ETH | 0.006 ETH | 0.024 ETH |
| Optimism | 0.048 ETH | 0.006 ETH | 0.024 ETH |
| Avalanche | 16 AVAX | 2.1 AVAX | 8 AVAX |
Token and NFT creation is priced differently: a base fee plus one increment per paid feature you enable, with NFTs at double the base. The page shows the exact number before you sign, and an unpriced network refuses to deploy rather than deploying free.
Network gas is always separate and always paid to the network, never to us.
Three tools, three questions. The Smart Contract Scanner asks what vulnerabilities exist? This one asks can an attacker actually exploit them? And AI Agent Security asks can the AI agent be manipulated into causing harm? They are not variations on a scanner — each starts where the last one stops.
A scan tells you what is wrong, line by line. It does not tell you the thing you actually need before launch: which combination of those lines lets somebody take the money. Real attacks are almost never one finding — they are a chain. Mango Markets was not "the oracle is thin"; it was a thin oracle, plus leverage, plus a position big enough to move it.
Two ways in. Hack my contract is the tool page: paste your source or an address and it does the whole thing — the scan runs in your browser, the chains come back underneath. Or run a normal scan on /scan and press Run Attack Simulation under the results. Either way it costs 5 credits.
The playbook is free and needs no account. Every chain the simulator can compose is written out in full on /attack — what has to be true first, how the attack runs, what it costs you, how you close it, and the real incident that went the same way. What the plan buys is having it run against your code and tell you which ones apply.
For every chain the scan can support:
| Field | What it is |
|---|---|
| The attacker's question | The thing being tested, in one line — "Is there anything I can set, with no permission check, that a money function later trusts?" |
| Confidence | How much of the chain the scan could prove — see below |
| Why this chain closes | Every precondition, each with the specific evidence that satisfied it — and the ones that were not established, shown too |
| Where | The functions in your contract the chain runs through |
| What it costs you | The consequence if it works, in plain terms |
| How the attack goes | The order of operations, conceptually — see the note on exploit code below |
| How to close it | The concrete fix for that chain, not generic advice |
| This has happened | A real, documented incident that ran the same way, linked to its full write-up |
| Family | Which of the 10 attack families it belongs to — Access Control, Reentrancy, Flash-loan, Oracle, Price, Signature/Replay, Upgradeability, Privilege Escalation, Business Logic, Economic/DeFi |
| Exploitability | Potential or Unverified — never "Confirmed". See below |
It is the share of that chain's preconditions the scan could prove, weighted by how load-bearing each one is. A chain at 100% is one where every link is backed by a detector hit or a hard structural fact. A chain at 70% has links resting on inference, and the report shows you which.
It is not a probability that an attack would succeed. That depends on live state — balances, liquidity depth, who holds the keys, whether the owner is a multisig — and none of that is visible to static analysis. A number carrying a meaning it cannot support is how a security tool starts lying, so this one is defined narrowly and says so on every result.
The obvious way to build this is to hand your source to a language model and ask it for attack scenarios. That produces confident, well-written, unfalsifiable output — including scenarios for code that is perfectly fine. One invented critical costs more trust than ten true mediums earn, and it buries the real finding.
So every scenario here is composed from evidence the scanner actually produced: detector hits, the parsed function table, access-control flags, and the call graph. A chain appears only when all of its required preconditions are satisfied, and it ships the evidence for each. If your code does not support the chain, no chain appears — and the report lists what it checked and ruled out, so you can tell a clean contract from an analysis that never ran.
Every path carries an exploitability verdict, and the strongest one this lane can reach is Potential. That is a deliberate ceiling.
| Verdict | What it means |
|---|---|
| Confirmed | A proof of concept was executed against an isolated copy of the contract and it worked. This lane never produces it — it reads your source and executes nothing, so the value exists only so a result that came from execution has somewhere to go. |
| Potential | Every required precondition is satisfied by something the scan can point at in your code, and most of the supporting evidence is present. The path is open; nobody has walked it. |
| Unverified | The required preconditions hold, but the chain rests largely on structural inference. A lead for a human reviewer. |
The distinction is not decoration. "Confirmed" starts an incident response; "Potential" starts a code review. Telling a team to do the first on evidence that only supports the second is how a security tool burns its welcome.
Everything is static and offline: the source is analysed in your browser and no transaction is constructed, signed or sent at any point, against mainnet or anywhere else. Where a future release executes a proof of concept it will run against a local fork or a testnet, never a live contract holding real money, and the verdict will say so. Testing a deployed address also requires an explicit authorization confirmation — a tool that maps how to attack a stranger's contract on request is a different product from this one.
The steps are conceptual: the order of operations an attacker would need, at the level a defender needs in order to close the path. That is a product decision as much as a safety one — someone fixing this needs to understand the shape of the attack, not to hold a working payload.
The 201-detector audit already runs client-side. The simulator is sent only the evidence that audit produced — rule ids, function names and visibilities, capability flags, the call graph. Never the code. The page builds that request through an explicit whitelist rather than posting the analysis object, and the server copies out the same fields again and discards everything else.
Silence is not a safety certificate. "No complete attack chain could be assembled" means these specific chains do not close with the evidence available — the individual findings in your scan still need fixing, and a logic bug unique to your protocol is outside any pattern library. That is what a manual audit is for.
Test AI Agents Before They Can Move Your Money. An agent with a wallet is a new kind of attack surface: the code is fine, the contract is fine, and the loss happens because a sentence inside a web page convinced the thing holding the key to send money somewhere. Nothing in a contract scanner looks at that.
The chain this tool tests, stage by stage: Prompt Injection → Tool Abuse → Transaction Manipulation → Wallet Signing → Privilege Escalation → Fund Loss. Each stage is tested independently, so the report can tell you where the problem starts rather than only that it ends badly.
| Lane | What it does | Cost |
|---|---|---|
| A · Analyze | Reads the agent's tool definitions and builds a capability graph: which tools move value, which pull in untrusted text, which touch keys, whether an approval gate exists. Reads MCP tools/list, OpenAI and Anthropic tool schemas, ElizaOS character files, n8n exports and agent source. | 5 credits |
| B · Probe | Sends 20 adversarial messages to your agent's endpoint and reads what comes back. The only lane that produces observed evidence. | 10 credits |
| C · Decoy signer | An RPC URL that looks like a wallet to your agent and holds no private key. Records and decodes every signature it attempts. | 10 credits / session |
An agent's configuration is one of the most sensitive files a team owns, and people paste .env-shaped things by accident. So the parsing runs on your machine: sfi-agentsec-graph.js is a generated browser build of the same module the server and the test suite use, and what crosses the network is the structure it produces — tool names, capability classes, booleans. The prompt text is not in that object at all. If a private key or an API credential is found in what you pasted, the page tells you the kind and the count, refuses to print the value, and tells you to rotate it.
Every payload tests one thing: whether the agent obeys text it should have treated as data. Obedience is the vulnerability; what an attacker would then ask for is just the payload of the day. So the probes ask for something harmless and unmistakable — echo a token, name a sentinel address (0x5afe…5afe, for which no private key exists), reach for a tool nobody asked for. The full corpus, with the reasoning behind each probe, is published on the playbook tab.
/.well-known/saferico-agent-test.txt on the endpoint's own origin, containing a token only your account can derive. Redirects are not followed.The endpoint is also checked against an SSRF guard: private, loopback, link-local, CGNAT and cloud-metadata addresses are refused, as are IPv6 literals and credentials embedded in the URL. Without that, "post a URL and we will fetch it from our infrastructure" is a free port scanner for every private network reachable from a Cloudflare egress.
Not "it is configured not to sign" — there is no private key in the module, so signing is not a rule it follows, it is an operation it cannot perform. It answers reads like an ordinary node and reports a balance worth moving; when the agent asks it to send or sign, it decodes the calldata against the 291-signature database, records the attempt, and returns an error rather than a plausible transaction hash. Fabricating a hash would be manufacturing evidence for a transaction that never happened. The attempt is the finding.
Only testnet chain ids are accepted, from a list written separately from the main chain table so that adding a mainnet to one cannot silently open it in the other. Sessions stop by themselves after 60 minutes, 200 recorded events, or 15 minutes idle.
Six categories — Prompt Injection, Tool Permissions, Transaction Controls, Wallet Security, Privilege Boundaries, Fund Loss Risk — each PASS, FAIL or NOT TESTED. Three states, not two, and the score prints its denominator: measured over 41 of 58 checks.
That matters more than it sounds. The naive implementation gives an agent nobody could probe a perfect score, because every failure needs evidence and there is none — a certificate handed out for being unreachable. And even with three states the arithmetic flatters: three easy checks passed out of fifty-eight is 100%. So below 40% coverage the score is withheld entirely and the report says why. A number is remembered; a caveat beside it is not.
A chain assembled only from the shape of a config — "this tool can send, and no gate was declared" — is a hypothesis. It might be wrong because the gate lives in code we never saw. So a chain with no observed evidence is capped at High however complete it looks, and labelled structural. Critical is reserved for chains where a probe response or a decoy-signer record shows the behaviour actually happened.
For every chain: Vulnerability · Severity · Attack Vector · Attack Path (each stage with the evidence that satisfied it, and whether that evidence was observed or inferred) · Impact · Evidence · Recommended Fix · Mitigation · Human Approval required. Plus the chains that were checked and ruled out, and every check that could not be measured — because a report listing only what it found leaves you unable to tell a clean agent from a scan that never ran.
Nothing here is executed against mainnet and no value moves at any point. A high score is not a certificate: it means these checks, on the configuration you pasted and the endpoint you attached, behaved on the day you ran them. Model behaviour is not deterministic — a probe that got a refusal today can get compliance tomorrow — so run it again after every prompt edit, new tool and model upgrade.
Every scan on this site is a photograph. You check a token, it comes back clean, and two days later the owner mints a billion more or pulls the liquidity. The report was never wrong — it was answering a question about a moment that had passed. A monitor watches the moment after.
Set one up on the Monitors tab of your account, or ask SAFI. You can watch 25 addresses at once, and every one is checked every 15 minutes.
| Alert | What triggers it | Severity |
|---|---|---|
| Ownership transferred | An OwnershipTransferred event. Renouncing to the zero address is reported as information, not as an alarm — it is good news. | high |
| New tokens minted | A Transfer whose sender is the zero address — which is what a mint actually is on-chain. | high |
| Paused / unpaused | Paused or Unpaused. A pause stops every holder transferring at once. | high |
| Proxy upgraded | Upgraded, AdminChanged or BeaconUpgraded — the code behind the address was replaced, so any earlier audit no longer describes what is deployed. | critical |
| Admin role granted | RoleGranted / RoleRevoked on an AccessControl contract. | medium |
| Liquidity removed | An LP Burn on the pool, and an hourly reading of the pool's actual depth — a pool can be emptied by a privileged function that emits no event at all, and the balance is the reading that cannot be sidestepped. Fires on a drop of more than 50%. | critical |
| Runtime code changed | An hourly hash of the deployed bytecode. For a normal contract this is impossible without a proxy upgrade or a SELFDESTRUCT-and-redeploy. | critical |
A wallet monitor watches approvals — the front door almost every drainer walks through. It alerts when the wallet grants an unlimited allowance, an allowance above a threshold you set, or setApprovalForAll on an NFT collection. Revocations (an approval of zero, or setApprovalForAll(false)) are never alerted, because they are the thing you want to happen.
This one is scoped to a wallet rather than a token on purpose. "Alert me on big approvals for this token" sounds like the same feature, but every holder's approval to every router would fire it. Filtered to one wallet, the query covers every token that wallet ever touches — including ones you have never mentioned to us.
Alerts are delivered on Telegram. Press Link Telegram on your account page — it hands you a one-tap link that opens the bot and attaches it to the account you are signed into. The code lasts 15 minutes and works once.
This paragraph used to say that opening the bot linked your account by itself. It did not. Signing in through Telegram created a second account with its own plan and its own history, so somebody who paid on the website and then opened the bot had two accounts and alerts on neither. The link above is the flow that actually joins them; it is new, and the old sentence was wrong for as long as it stood.
Every alert is also kept in the feed on your account page whether or not Telegram is linked, and anything detected before you link is delivered once you do — undelivered alerts are drained on a later 15-minute tick rather than dropped. If the Telegram account you are linking is already a SaferICO account with a plan or saved reports on it, the link is refused and says what is on it, because moving it would strand that.
It notifies. It never signs. Reacting to an alert on your behalf would mean holding your key, and SaferICO does not take custody of keys or funds — the same rule that governs automations and every tool on this site. You get told; you decide.
Honest limits: a check runs every 15 minutes, so this is not front-running protection — it is knowing within minutes instead of finding out from a price chart. Events are read from public RPC endpoints; if every endpoint for a chain is unreachable the watcher retries the same block range rather than skipping it, and the account page shows the last error.
Nobody opens a website and pastes a contract into a textarea before every push. A check that appears on the pull request, though, is in the team's way in the good sense — every day. This runs all the same detectors as the web scanner on every commit.
name: Security
on: [push, pull_request]
jobs:
saferico:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SaferICO security scan
run: curl -fsSL https://saferico.com/sfi-ci.mjs -o sfi-ci.mjs && node sfi-ci.mjs --sarif saferico.sarif
env:
SAFERICO_API_KEY: ${{ secrets.SAFERICO_API_KEY }}
# optional: findings appear on the Security tab and inline on the diff
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: saferico.sarif
Create the key on the API keys tab of your account and add it to your repository secrets. Nothing is installed: the runner has no dependencies, because a security tool that pulls its own dependency tree into your pipeline is arguing against itself.
This is the part worth being precise about. The detectors are downloaded to your machine and executed there. What is sent back to SaferICO is a count per severity and a list of rule ids with file names and line numbers — never the code, and never the evidence strings, because those quote your source. Those stay local, in the SARIF file and the job summary. The test suite asserts this by scanning the outgoing request body for source fragments.
| Flag | Environment variable | Default | What it does |
|---|---|---|---|
--path | SAFERICO_PATH | . | Where to look for .sol files |
--fail-on | SAFERICO_FAIL_ON | high | critical, high, medium, low or none — the severity at or above which the build fails |
--sarif | SAFERICO_SARIF | off | Write SARIF 2.1.0 for GitHub code scanning |
--json | SAFERICO_JSON | off | Write the full local result, evidence included |
--exclude | SAFERICO_EXCLUDE | — | Extra comma-separated path fragments to skip |
--max-files | SAFERICO_MAX_FILES | 400 | Stop after this many files |
--no-report | SAFERICO_NO_REPORT | off | Scan, but record nothing on your dashboard |
node_modules, lib, out, cache, artifacts, build output, and test files (*.t.sol, *.s.sol, test/, mocks/) are skipped. Findings inside somebody else's audited library are noise, and a report full of noise is one nobody reads twice.
| Code | Meaning |
|---|---|
0 | Clean, or findings below your gate |
1 | The gate was crossed — the build should fail |
2 | The scan could not run: no key, wrong plan, no Solidity found, network failure |
1 and 2 are separate on purpose. "We found a critical bug" and "the scanner never ran" must never look the same to a pipeline — a missing secret that silently reports success is worse than no scanner at all. Note that an empty directory exits 2, not 0.
Inline annotations on the changed lines, a job summary table, and — if you upload the SARIF — findings on the repository's Security tab with full descriptions. Every run is also recorded on the CI tab of your account page with its repo, branch, commit and verdict, so you can see when a number started climbing.
Licensing, not DRM: the runner requires a key and records runs against your account. The detectors themselves are already public at /sfi-engine.js and we are not going to pretend otherwise. What a subscription buys is the licence to use them commercially, plus the history, the gate and the hosted half.
Most scams are not verified. That is a real hole in every source-reading scanner, including ours: the contracts most worth checking are exactly the ones there is no source to read, and the honest answer has always been "UNVERIFIED — treat as high risk", which is useless. Deployed bytecode is always public. This reads that instead.
Ask SAFI ("check this unverified contract"), or call it directly:
curl -X POST https://saferico.com/api/v1/tools/bytecode_scan \
-H "Authorization: Bearer sfi_live_…" \
-H "Content-Type: application/json" \
-d '{"address":"0x…","network":"bnb"}'
| Category | What is read out of the code |
|---|---|
| Exposed functions | The 4-byte selectors in the contract's dispatcher, matched against a dictionary of mint, blacklist, pause, trading-toggle, fee, limit, upgrade and withdrawal signatures — so a hidden setBots(address[],bool) is named even with no source. |
| Dangerous opcodes | SELFDESTRUCT, DELEGATECALL, CALLCODE, CREATE2, and ORIGIN (tx.origin, which is phishable when used for authorisation). |
| Proxies | EIP-1967, EIP-1822 (UUPS), legacy ZeppelinOS and EIP-1167 minimal clones — detected from the storage slot constant compiled into the code. A minimal proxy reports its target so you can analyse the contract that actually runs. |
| Delegated wallets | An EIP-7702 account is identified as a wallet, not a contract, with the address whose code it runs. |
| Compiler | The solc version and whether a source hash (IPFS or Swarm) was published, read from the metadata trailer. |
| Hard-coded addresses | Every address compiled into the code — routers, treasuries, the burn address. |
It proves which functions a contract exposes and which opcodes it contains. It cannot tell you what those functions do, or who is allowed to call them. Access control lives in the control flow, and reconstructing that from bytecode is decompilation, which this is not.
So a result reads "a mint entry point exists", never "the owner can mint" and never "this is a scam". Treat every power it lists as a capability to go and verify on-chain. A tool that overstates its reach is worse than no tool, because you would act on it.
Every 4-byte selector and 32-byte storage slot in the dictionary is recomputed from its signature with keccak-256 by the test suite on every build. A wrong constant would not throw — it would silently report "no mint function" about a contract that has one, and a false negative on a security tool is the worst bug it can ship.
A read-only API over the same tools SAFI uses. Create a key from your account page and send it as a bearer token.
curl https://saferico.com/api/v1/tools \
-H "Authorization: Bearer sfi_live_…"
GET /api/v1/tools lists every tool your plan can call, with its parameters and your remaining credits. An API nobody can enumerate is an API nobody adopts.
POST /api/v1/tools/{name} with a JSON body of arguments.
curl -X POST https://saferico.com/api/v1/tools/token_info \
-H "Authorization: Bearer sfi_live_…" \
-H "Content-Type: application/json" \
-d '{"chainId":56,"address":"0x…"}'
| Tool | What it returns |
|---|---|
token_info | A token's on-chain facts: name, symbol, decimals, total supply |
check_honeypot | Simulates a buy and a sell to detect honeypot behaviour |
contract_source | Verification status, compiler, proxy detection, source |
portfolio_scan | Native balance and USD value across supported chains |
market_info | Live native-coin prices and gas per chain |
bytecode_scan AI Agents | Reads an unverified contract from its deployed bytecode: exposed functions, dangerous opcodes, proxy shape, compiler |
The same key drives scanning inside your CI. POST /api/v1/ci/session opens a run and returns your licence state and the detector bundle URLs; POST /api/v1/ci/report records the result and returns the verdict. You do not normally call these yourself — sfi-ci.mjs does.
Anything that produces a transaction is not on the API and will return 403 not-available-over-api. A transaction needs a wallet in a browser, and an API key that could move funds is a key that can be stolen to move funds.
| Status | Meaning |
|---|---|
401 | Missing or invalid key |
402 | The plan behind the key no longer includes API access |
403 | That tool is not available over the API |
404 | Unknown tool name |
429 | Monthly credits exhausted |
Every call spends credits from the same monthly allowance as the agent.
openapi.json is generated from the same tool registry the server executes, and a deploy fails if the two disagree — so an SDK generated from it cannot describe an API that does not exist. Try any endpoint in the playground.
The full 201-detector audit, on your own machine. No account, no key, nothing uploaded.
npx saferico scan ./contracts
The detector engine is downloaded from this site, verified against a published SHA-256, and executed locally in a sandboxed VM context. What leaves your machine is one request for the engine files — not your source, not your findings, not your file names.
| Flag | Meaning |
|---|---|
--fail-on <sev> | critical, high, medium, low or none (default high) |
--json | Print the full result as JSON instead of a table |
--output <file> | Write the JSON result to a file |
--sarif <file> | Write SARIF 2.1.0 for GitHub code scanning |
--engine-version <v> | Pin the engine for a reproducible scan |
--exclude <a,b> | Extra path fragments to skip |
--max-files <n> | Stop after n files (default 400) |
--config <file> | Read options from a JSON file; flags win |
--quiet / --verbose | Less / more progress on stderr |
| Code | Meaning |
|---|---|
0 | Clean, or findings below the gate |
1 | The gate was crossed — the build should fail |
2 | The tool could not run: bad path, unreachable engine, bad arguments |
- name: SaferICO security scan
run: npx saferico scan ./contracts --sarif saferico.sarif --fail-on high
- name: Upload to code scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: saferico.sarif
This needs no API key. The GitHub Action is the other way to do the same thing, and that one records each run against your account.
Every operation in the API, rendered as a form at /playground. The form is built from openapi.json when the page loads, so it cannot describe an argument the server does not accept — the two are the same document.
Reading the request and response shapes needs nothing. Actually calling an endpoint needs a key, and therefore the Business plan or above; the call is metered exactly like the same call from your own code.
| What | Behaviour |
|---|---|
| Your key | Held in memory in that browser tab only. Never stored, never sent anywhere but the endpoint you invoke. |
| Execution | Nothing is called until you press Execute — opening the page costs no credits. |
| Cost | The same as calling the endpoint directly: 2 credits for a read tool. |
| Drift | Impossible by construction. A deploy fails if openapi.json and the tool registry disagree. |
All six developer surfaces — CLI, local MCP, hosted MCP, REST API, this playground and the discovery files — are summarised side by side on the developers page, including which plan opens which.
A gate that cannot tell “the code changed” from “the detectors changed” is not much of a gate. Every engine release is archived under a version and hashed.
npx saferico engines
npx saferico scan ./contracts --engine-version 2026.09.01
engine/manifest.json lists every published version with the SHA-256 of each file. Pinned or not, every downloaded file is checked against it — a mismatch is a hard refusal, because a tool that downloads code and runs it must never treat “could not verify” the same as “verified”.
Asking for a version that does not exist is an error rather than a silent fall back to the newest: a pin that quietly stops pinning is worse than no pin. In CI, POST /api/v1/ci/session accepts the same engineVersion; omit it and you always get the newest detectors, which is what most pipelines want.
SaferICO speaks the Model Context Protocol, so its tools can be called from inside Claude, Cursor or any MCP client. There are two servers, and the difference between them is the whole design.
{
"mcpServers": {
"saferico": {
"url": "https://saferico.com/mcp",
"headers": { "Authorization": "Bearer sfi_live_…" }
}
}
}
Same tools, same plan gates and same credit meter as the REST API. POST-only JSON-RPC over Streamable HTTP; protocol revisions 2026-07-28 back to 2025-03-26 are all served. The server card is at mcp.json.
{
"mcpServers": {
"saferico": { "command": "npx", "args": ["-y", "saferico", "mcp"] }
}
}
Tools: scan_path, scan_source, engine_info. No API key, and nothing is uploaded.
An edge request gets 10 ms of CPU; the audit needs orders of magnitude more. Rather than work around that, the split is deliberate: the hosted server carries reads that are cheap and need live chain data, and the heavy engine runs where the CPU is free and the source is already sitting — your browser, your CI runner, or your machine. Asking the hosted server for audit_contract returns a clear refusal, not a partial result.
Four files describe this platform to software rather than to people. All four are generated from the tool registry, and all four are static — reading them costs nothing and cannot fall behind the code.
| File | For |
|---|---|
| /llms.txt | A plain-text briefing: what the tools are, what they cost, where the heavy one runs, and what the product refuses to claim |
| /agent.json | Structured capabilities, interfaces, pricing, networks — and an explicit list of what is not reachable remotely, and why |
| /mcp.json | The MCP server card: endpoint, protocol revisions, auth, tool schemas, and ready-to-paste client config |
| /openapi.json | OpenAPI 3.1 for the REST API, for SDK generation and no-code tools |
Every one of them states the limits as plainly as the features. An agent that learns from a document that the full audit is local does not waste a call finding out, and does not tell its user the wrong thing.
@SaferICOAuditBot is the platform in your pocket: scanning, token and NFT deployment, staking, markets, invoices and SAFI chat.
A Chrome side panel (v0.6.0) with the Transaction Guard, scanning, approval checks, markets and the agent, so you can check a contract without leaving the page you are on. It also draws SaferICO's verdict directly onto Etherscan-family explorers and DexScreener.
Network requests are made by the extension's service worker rather than the page, which is why it can read explorer and market APIs directly. It talks to saferico.com for anything that needs your plan.
Every scan answers "was this contract safe, at some point". The Transaction Guard answers what is about to happen to my money, at the only moment it can still be stopped.
When a site asks your wallet to sign, the request is paused and read first: which function, which spender, whether the allowance is unlimited, whether the contract published its source, and how many days ago it was deployed. You get one sheet and you decide. It watches only the six methods that move money — eth_sendTransaction, the three signTypedData spellings, personal_sign and eth_sign — and passes everything else straight through untouched.
A contract created in the last two days asking for an unlimited-style permission is called out at the top, in those words. That single sentence is the shape of nearly every drain.
{code: 4001}, so the site takes its normal "user rejected" path. Nothing here ever resolves with a made-up transaction hash.It is off by default and it is not part of what the extension asks for at install. Sitting between a site and a wallet means running on the page you are on, whichever page that is — so that permission is requested only when you switch the guard on in the panel, and switching it off unregisters the scripts rather than muting them. You can withdraw the permission at any time from chrome://extensions; the panel then shows "switched on, but not running" rather than a green tick over nothing.
A static read, not a replay on a forked chain — a service worker cannot fork a chain, and a simulation that silently fell back to guessing would be worse than none. It can tell you what you are granting; it cannot know what someone will later do with it. Anything it could not establish is listed under Not checked instead of counted as safe.
A wallet shows you 0x095ea7b3… and asks you to sign. This turns those four bytes into a function name, and the rest into arguments you can read.
291 signatures — 216 functions, 34 events and 41 custom errors — covering ERC-20/721/1155/4626, ownership and roles, pausing, blacklists, fees, proxies and upgrades, DEX routers, staking, governance, bridges, multisig and account abstraction, plus the shapes drainer contracts use.
Not one hash in that table was typed by hand. The corpus is a list of signature strings, and every selector and topic is computed from it with keccak-256 at build time; the deploy fails if the generated table has drifted from the strings that produce it. A mistyped selector never throws — it just silently stops matching, and reports "unknown function" about a drain.
A selector maps to a list of signatures, never one. Giving a malicious function a name whose selector matches a harmless well-known one is a known trick, so when more than one signature matches, all of them are returned and the answer is marked ambiguous. Nothing picks a winner for you.
Arguments decode completely or not at all. There is no partial render, because a half-decoded argument list looks exactly like a full one. An address word with non-zero upper bytes, a bool that is neither 0 nor 1, an offset past the end of the data — each is refused rather than rendered. A selector we have never seen is reported as unknown, and that is never softened into "safe".
An unlimited approve, a setApprovalForAll(true), a permit (a signature alone is enough, and it never appears in your transaction history the way an approval does), an upgradeTo, a transfer to the zero address. Revert data decodes too — Error(string) gives the real message and Panic(0x11) is named as an arithmetic overflow rather than left as hex.
GET /api/sig?q= · POST /api/sig/decode · POST /api/sig/revert · GET /api/sig/stats
Almost every ERC-20 is OpenZeppelin plus a few dozen lines. So "these two contracts are 97% similar" is true, useless, and mostly a measurement of how popular OpenZeppelin is. The signal is in what is left when library code is removed.
Winnowing — the algorithm behind MOSS. Source is tokenised, hashed in overlapping 12-token k-grams, and one fingerprint is selected per 8-gram window. Two properties follow: any shared run of 19 tokens or more is guaranteed to be found, and renaming every variable or reformatting the whole file changes nothing, because the comparison runs on token structure rather than text.
The fingerprints of 20 library contracts — OpenZeppelin's token, ownership, guard and proxy primitives, the legacy SafeMath, and the Uniswap V2 interfaces every meme token embeds — are removed before any figure is reported. Measured on two unrelated tokens built on the same library, that takes the score from 0.67 to 0.15, while a renamed copy of the custom half stays at 1.00.
Those library sources are reproductions, and where a project's version differs from ours the code counts as novel rather than as library. That direction is deliberate: being too eager would subtract a scammer's modified ERC-20 and hide the exact lines worth reading.
The novelty report answers what an auditor asks first — what am I reading that nobody has read? It returns the line ranges matching no library we hold. Those are where a modification to a standard contract would be. They are not findings, and nothing says the code in them is wrong.
Never "clone", "fork", "copy" or "scam". Similarity is a measurement; who copied whom, and whether copying was wrong, is not on the chain. Reusing a well-known contract verbatim is the safest thing a deployer can do, and the same number describes that too. Even at a 90%+ match the output states what it has not established: which was written first, and whether the reuse was authorised.
POST /api/similarity/compare · POST /api/similarity/novelty · GET /api/similarity/corpus
Where did this money come from, and what is it near? Funding traces and shortest paths, with the lists kept as data rather than compiled into the code.
Every address on such a list is an accusation against whoever holds it. One transcribed character, one pool address copied from the wrong chain, one entry kept after a delisting, and the tool tells a user that an innocent wallet is sanctioned. That is not a score being wrong; it is a false accusation, published by us, about a specific person.
So label lists are operator-supplied, loaded into KV, and a list is refused at load time unless it says where it came from and when it was loaded. Every match carries that provenance. When no list is loaded the report says so in those words — listsConsulted: [] and matches: [] are different facts, and both are always reported, so "screened, nothing found" can never be confused with "nothing was screened".
Most of the value. A funding trace walks back up to 3 hops to whatever first paid an address, with the transaction proving each hop. Path finding gives the shortest transaction path between two addresses you name — no list, so no accusation. And the structural labels (the zero address, burn addresses, precompiles) are facts rather than anyone's opinion.
A graph walk over a chain is unbounded by nature, so each report is capped at 14 explorer lookups, 3 hops and 25 counterparties per node, and reports what it spent. "No path was found within these limits" is a true statement; "there is no path" is not one it can make.
Never "criminal", "launderer", "dirty" or "clean". One hop from a mixer describes an activist, an ordinary privacy-minded user and a thief identically, and the chain does not record which. Every report ends on the same sentence: distance is not guilt, and a ledger does not record intent.
GET /api/taint?chain=&address= · POST /api/taint/path · GET /api/taint/lists
Point the scanner at a public GitHub repository instead of pasting one file at a time.
The server resolves the repository to a file list — one API call, cached — and your browser fetches each file from raw.githubusercontent.com and scans it locally. The file contents never touch our servers.
Signing in with GitHub asks for the read:user scope only, and the access token is discarded straight after reading your profile. This endpoint sends no Authorization header at all, so it could not read a private repository even by accident. For private code, the CI scanner runs inside your own runner and posts back nothing but counts.
Every .sol file is classified — source, interface, dependency, test, mock, script, example — and all of them are counted. Source, interface and dependency are selected by default, because vendored code under lib/ is code that gets deployed. Tests and mocks are present in the counts and selectable, just not scanned by default.
GitHub truncates the file tree for very large repositories, and that flag is passed straight through with a note saying the list is incomplete — a scan built on a truncated tree cannot support a statement about the repository as a whole.
Unauthenticated GitHub allows 60 requests an hour per IP, shared across an edge location, so being rate limited is routine. It is reported as rate-limited with the reset time and HTTP 429 — never as an empty file list, and never as "repository not found". A 404 says plainly that it cannot distinguish "private" from "missing".
Per-file findings aggregate into one report. A rule firing in 40 files is one project-level finding, not 40 — otherwise a style rule that matches everything outranks a single critical. Files that failed to fetch are counted separately and never folded into "0 findings". The project rating is the worst single finding, not an average: three hundred clean files do not offset one critical.
GET /api/repo/files?repo=owner/name · POST /api/repo/aggregate
The Watchtower watches the chain. But the largest theft in this industry never touched a contract — Bybit lost $1.43B to a tampered JavaScript bundle served from a compromised bucket. The contract was fine. The front end was not.
Websites change every day, and a monitor that alerts on every diff is one nobody reads. Severity tracks what a change enables: nameservers, final host and a new third-party script are critical; DNS records and a removed security header are high; an added header or a content change is information. Critical alerts always offer the innocent explanation first — "if nobody on your team did this" — because being certain about an attack is not something a probe can be.
One failed probe from one edge location is not evidence a site is down, so a failure must repeat on the next check before it becomes an event. A failed probe also never overwrites the baseline — otherwise the recovery, and every later comparison, would have nothing to compare against.
Certificate expiry. A Cloudflare Worker cannot read the certificate of a site it fetches — the TLS details available describe the inbound request, and a subrequest exposes no peer certificate. Every competing product advertises SSL-expiry alerts; shipping one that silently never fires would be worse than not having it. A failed handshake is detected and reported. The expiry date is reported as unavailable, with the reason, everywhere.
The Sites tab in your dashboard, beside the chain Watchtower. Test it now runs a single probe and shows you the answer — status, redirect chain, every external script origin and any missing security header — without spending one of your fifteen slots or storing anything.
Up to 15 sites, checked every 15 minutes, alerts to Telegram. GET/POST /api/sitewatch · POST /api/sitewatch/probe · POST /api/sitewatch/toggle · POST /api/sitewatch/delete · GET /api/sitewatch/events
A landing page for each of the seven mainnets, at /ethereum-smart-contract-audit and its siblings, linked from /chains.
Each is generated from data that genuinely differs by chain: the incidents that happened there with real losses, the public RPC endpoints verified for it, the DEX router the honeypot simulation actually uses, its testnet and whether our faucet funds it, and whether the free explorer plan serves JSON-RPC there.
And each says what is the same everywhere. The detector set is identical on every chain — there are no chain-specific rules, and the pages state that rather than implying otherwise. The generator refuses to publish if any two pages are more than 80% identical, because a set of near-identical landing pages is a doorway pattern that costs the whole domain rather than earning a ranking.
The public RPC did not answer. This is a connection problem, not a verdict on the contract you were looking at — nothing has been lost and nothing is wrong with your address. Wait a moment and try again.
An address was typed with the wrong upper/lower-case mix. Copy it from the explorer rather than retyping it.
If it is the native coin, a little is held back for gas — spending the whole balance leaves nothing to pay for the transaction itself. Give the balance a second to load before clicking Max.
Some tokens (USDT and its clones) refuse to change a non-zero allowance directly. The swap page resets the allowance to zero first and then approves; if you are approving elsewhere, do the same.
You are probably on a testnet. Market data comes from aggregators that only index mainnets.
They live in your browser. Clearing site data, or a different browser or profile, means a different store. Export your watchlist to keep a copy.
That list is also browser-local. The contract is fine — open it by address, and it will be remembered again.
A score is a summary of what automated checks found. It cannot know your intent, cannot judge business logic, and cannot see the future of an upgradeable contract. Treat a clean result as "no known pattern fired", not as "safe".
An audit describes the code as it was on the day it was read. A redeployment, a proxy upgrade or an owner change makes it a historical document. Check that the address you are trusting is the address that was audited.
Found a bug, or a contract behaving badly? Message us on Telegram. Average response is under 24 hours.