Skip to content

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:

FieldMeaning
auditId16-char uuid slice — the public report id
certificateId16-char uuid slice — the public certificate id
walletRequesting wallet, used for quota
scoreOverall security score
findings[]Each with severity, title, description, location
severityCounts{ critical, high, medium, low, informational }
certificateEligibleDerived — see below
statussuccess | failed

Certificate eligibility

js
certificateEligible = severityCounts.critical === 0 && severityCounts.high === 0

The 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:

EndpointReturns
GET /v1/audit/report/:idAudit JSON
GET /v1/audit/certificate/:idCertificate 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

SurfaceFile
Chat carddefi-dex/src/component/defi/swapAI/actions/AuditResult.tsx
Public reportdefi-dex/src/pages/audit/AuditReport.tsx/report/:id
Public certificatedefi-dex/src/pages/audit/AuditCertificate.tsx/certificate/:id

Wiring

LayerLocation
Intentbz-agent/src/utils/constant.tsCONTRACT_AUDIT
Templatebz-agent/src/template/contractAudit.ts
NodecontractAuditNode in bz-agent/src/agent/node.ts
Graphbz-agent/src/agent/graph.ts
ActionBLAZ_ACTIONS.CONTRACT_AUDIT = 'contract_audit'
Servicebz-backend/v1/services/contractAudit.service.js
Modelbz-backend/models/contractAuditModel.js
Routesbz-backend/v1/routes/audit.route.js

Environment variables

VariablePurpose
OPENAI_API_KEYCredential for the audit model
ETHERSCAN_API_KEYEtherscan v2 multi-chain key for source fetch
AUDIT_PUBLIC_BASE_URLShare-link prefix. Optional; defaults to https://audit.blazpay.ai

Intent boundaries

The intent description carries explicit negatives, because these get confused:

User saysCorrect 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/:id and /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.

Internal + developer documentation. Usage figures are point-in-time snapshots — re-verify before publishing them.