Appearance
Contract auditor
An in-chat EVM smart-contract security review. A user says "audit 0x… on bsc" or pastes Solidity; BlazAI returns a score, findings, a shareable report link, and — if nothing critical or high was found — a certificate.
Intent: CONTRACT_AUDIT · Action: contract_audit · Cost: free, quota-limited
Why it exists
A free scanner inside BlazAI drives top-of-funnel adoption and produces shareable assets. A project that gets a clean Blazpay certificate has a reason to link back; a project that gets findings has a reason to return. Both outcomes are useful.
The flow
Source acquisition
Verified source is fetched through the Etherscan v2 unified API — one key, all supported chains:
https://api.etherscan.io/v2/api?chainid={chainId}&module=contract&action=getsourcecode&address=…Supported chains: Ethereum, BSC, Polygon, Arbitrum, Optimism, Base, Linea, Avalanche, Fantom.
Etherscan returns multi-file contracts as a JSON blob. The service flattens them with // File: <path> headers so the LLM sees a single coherent document with provenance preserved.
Source is clamped to 40,000 characters
Anything longer is truncated, and the truncation flag is stored on the audit document. A large protocol therefore gets a partial audit, and the report should be read with that in mind. The flag exists precisely so nobody mistakes a truncated audit for a complete one.
Output
Persisted in bz-backend/models/contractAuditModel.js:
| Field | Meaning |
|---|---|
auditId | 16-char uuid slice — the public report id |
certificateId | 16-char uuid slice — the public certificate id |
wallet | Requesting wallet, used for quota |
score | Overall security score |
findings[] | Each with severity, title, description, location |
severityCounts | { critical, high, medium, low, informational } |
certificateEligible | Derived — see below |
status | success | failed |
Certificate eligibility
js
certificateEligible = severityCounts.critical === 0 && severityCounts.high === 0The certificate page 404s when not eligible. There is deliberately no "certificate with caveats" — a certificate either means something or it does not.
Quota
Per wallet, configurable through system settings:
js
await SystemSetting.setValue('audit_free_limit_per_wallet', 5)Default is 3. GET /v1/audit/usage?wallet= returns { used, limit, remaining }.
Failed audits do not consume quota
A failed audit still creates a document with status: 'failed', but getAuditUsage counts with status: { $ne: 'failed' }. A user whose audit crashed because Etherscan timed out is not punished for it.
Routes
All public, no auth, mounted at /v1/audit:
| Endpoint | Returns |
|---|---|
GET /v1/audit/report/:id | Audit JSON |
GET /v1/audit/certificate/:id | Certificate JSON — only if certificateEligible |
GET /v1/audit/usage?wallet= | { used, limit, remaining } |
Share URLs
{AUDIT_PUBLIC_BASE_URL}/report/{auditId}
{AUDIT_PUBLIC_BASE_URL}/certificate/{certificateId}AUDIT_PUBLIC_BASE_URL defaults to https://audit.blazpay.ai. These pages are routed outside MainLayout in defi-dex/src/routes/index.tsx so they render unauthenticated on the public host.
Share links are built client-side and are origin-relative, which is why the AI domain split needed no work here — they rewrite themselves.
Surfaces
| Surface | File |
|---|---|
| Chat card | defi-dex/src/component/defi/swapAI/actions/AuditResult.tsx |
| Public report | defi-dex/src/pages/audit/AuditReport.tsx → /report/:id |
| Public certificate | defi-dex/src/pages/audit/AuditCertificate.tsx → /certificate/:id |
Wiring
| Layer | Location |
|---|---|
| Intent | bz-agent/src/utils/constant.ts → CONTRACT_AUDIT |
| Template | bz-agent/src/template/contractAudit.ts |
| Node | contractAuditNode in bz-agent/src/agent/node.ts |
| Graph | bz-agent/src/agent/graph.ts |
| Action | BLAZ_ACTIONS.CONTRACT_AUDIT = 'contract_audit' |
| Service | bz-backend/v1/services/contractAudit.service.js |
| Model | bz-backend/models/contractAuditModel.js |
| Routes | bz-backend/v1/routes/audit.route.js |
Environment variables
| Variable | Purpose |
|---|---|
OPENAI_API_KEY | Credential for the audit model |
ETHERSCAN_API_KEY | Etherscan v2 multi-chain key for source fetch |
AUDIT_PUBLIC_BASE_URL | Share-link prefix. Optional; defaults to https://audit.blazpay.ai |
Intent boundaries
The intent description carries explicit negatives, because these get confused:
| User says | Correct intent |
|---|---|
| "audit 0x…", "scan my contract for vulnerabilities", "is this contract safe" | CONTRACT_AUDIT |
| "what is a smart contract audit?" | CONVERSATION |
| "rugcheck 0x…", "is this token a honeypot" | TOKEN_SAFETY |
Known limits
- It is an LLM review, not a formal audit. It finds patterns; it does not prove absence of bugs. Positioning it as a substitute for a professional audit would be dishonest, and the certificate wording should reflect that.
- 40k-character clamp means large protocols get a partial pass.
- Verified source only for the address path. An unverified contract cannot be audited by address; the user must paste source.
- No re-audit versioning. Auditing the same address twice creates two independent documents with no diff.
- Social bots see the global OG fallback on
/report/:idand/certificate/:id— there is no server-side OG rendering yet, so a shared report unfurls generically. - No on-chain attestation. The certificate is a web page, not an NFT or a signed attestation. A project could screenshot a clean certificate and later upgrade the contract.