SmartContractAudit / server /index.html
ajaxwin
fix: Update file paths and ensure model loading in PropertyRetriever
45bd962
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>SC Audit RL Environment</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet" />
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2L4.5 5.5V10.5C4.5 15.14 7.7 19.46 12 20.5C16.3 19.46 19.5 15.14 19.5 10.5V5.5L12 2Z' fill='none' stroke='%2300ff88' stroke-width='2'/%3E%3Crect x='10.5' y='9.5' width='3' height='3' rx='0.5' fill='%2300ff88'/%3E%3C/svg%3E">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0a0c0f;
--surface: #111418;
--border: #1e2530;
--accent: #00ff88;
--accent2: #00b8ff;
--warn: #ffaa00;
--danger: #ff4455;
--text: #c8d0dc;
--muted: #4a5568;
--mono: 'Space Mono', monospace;
--sans: 'DM Sans', sans-serif;
}
html { scroll-behavior: smooth; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--sans);
font-size: 15px;
line-height: 1.6;
min-height: 100vh;
overflow-x: hidden;
}
/* ── noise overlay ── */
body::before {
content: '';
position: fixed; inset: 0;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 0;
opacity: .5;
}
/* ── grid lines ── */
body::after {
content: '';
position: fixed; inset: 0;
background-image:
linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 40px 40px;
pointer-events: none;
z-index: 0;
opacity: .35;
}
/* ─── layout ─── */
.wrap { position: relative; z-index: 1; max-width: 1100px; margin: 0 auto; padding: 0 28px; }
/* ─── header ─── */
header {
border-bottom: 1px solid var(--border);
padding: 18px 0;
}
.nav {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
font-family: var(--mono);
font-size: 13px;
color: var(--accent);
letter-spacing: .05em;
display: flex;
align-items: center;
gap: 8px;
}
.logo-dot {
width: 8px; height: 8px;
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 8px var(--accent);
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,100% { opacity: 1; }
50% { opacity: .35; }
}
.nav-links {
display: flex;
gap: 24px;
list-style: none;
}
.nav-links a {
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
text-decoration: none;
letter-spacing: .08em;
text-transform: uppercase;
transition: color .2s;
}
.nav-links a:hover { color: var(--accent); }
/* ─── hero ─── */
.hero {
padding: 90px 0 70px;
position: relative;
}
.hero-tag {
font-family: var(--mono);
font-size: 11px;
color: var(--accent);
letter-spacing: .12em;
text-transform: uppercase;
margin-bottom: 22px;
display: flex;
align-items: center;
gap: 10px;
}
.hero-tag::before {
content: '';
display: inline-block;
width: 28px; height: 1px;
background: var(--accent);
}
h1 {
font-family: var(--mono);
font-size: clamp(28px, 5vw, 52px);
font-weight: 700;
line-height: 1.15;
color: #fff;
margin-bottom: 26px;
letter-spacing: -.02em;
}
h1 span {
color: var(--accent);
text-shadow: 0 0 30px rgba(0,255,136,.3);
}
.hero-sub {
font-size: 16px;
color: var(--muted);
max-width: 560px;
line-height: 1.7;
margin-bottom: 40px;
}
.hero-badges {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 44px;
}
.badge {
font-family: var(--mono);
font-size: 11px;
padding: 5px 12px;
border-radius: 3px;
letter-spacing: .06em;
}
.badge-green { background: rgba(0,255,136,.08); color: var(--accent); border: 1px solid rgba(0,255,136,.25); }
.badge-blue { background: rgba(0,184,255,.08); color: var(--accent2); border: 1px solid rgba(0,184,255,.25); }
.badge-yellow { background: rgba(255,170,0,.08); color: var(--warn); border: 1px solid rgba(255,170,0,.25); }
.cta-row { display: flex; gap: 14px; flex-wrap: wrap; }
.btn {
font-family: var(--mono);
font-size: 12px;
padding: 11px 22px;
border-radius: 4px;
text-decoration: none;
letter-spacing: .06em;
transition: all .2s;
cursor: pointer;
border: none;
}
.btn-primary {
background: var(--accent);
color: #000;
font-weight: 700;
}
.btn-primary:hover {
background: #fff;
box-shadow: 0 0 24px rgba(0,255,136,.4);
}
.btn-outline {
background: transparent;
color: var(--text);
border: 1px solid var(--border);
}
.btn-outline:hover {
border-color: var(--accent);
color: var(--accent);
}
/* ─── section ─── */
section { padding: 64px 0; border-top: 1px solid var(--border); }
.section-label {
font-family: var(--mono);
font-size: 10px;
color: var(--muted);
letter-spacing: .14em;
text-transform: uppercase;
margin-bottom: 36px;
}
h2 {
font-family: var(--mono);
font-size: 22px;
font-weight: 700;
color: #fff;
margin-bottom: 8px;
}
h3 {
font-family: var(--mono);
font-size: 14px;
color: #fff;
margin-bottom: 10px;
}
/* ─── task cards ─── */
.task-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 18px;
margin-top: 32px;
}
.task-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 26px;
position: relative;
overflow: hidden;
transition: border-color .25s, transform .25s;
}
.task-card:hover {
border-color: var(--accent);
transform: translateY(-3px);
}
.task-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 2px;
}
.task-card.easy::before { background: var(--accent); }
.task-card.medium::before { background: var(--warn); }
.task-card.hard::before { background: var(--danger); }
.task-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 14px;
}
.task-num {
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
}
.diff-badge {
font-family: var(--mono);
font-size: 10px;
padding: 3px 9px;
border-radius: 99px;
letter-spacing: .07em;
text-transform: uppercase;
}
.diff-easy { background: rgba(0,255,136,.1); color: var(--accent); }
.diff-medium { background: rgba(255,170,0,.1); color: var(--warn); }
.diff-hard { background: rgba(255,68,85,.1); color: var(--danger); }
.task-desc {
font-size: 13px;
color: var(--muted);
line-height: 1.65;
margin-bottom: 18px;
}
.task-actions {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.action-chip {
font-family: var(--mono);
font-size: 10px;
padding: 3px 8px;
border-radius: 3px;
background: rgba(255,255,255,.04);
color: var(--muted);
border: 1px solid var(--border);
}
/* ─── endpoints table ─── */
.endpoint-list { margin-top: 28px; display: flex; flex-direction: column; gap: 2px; }
.endpoint-row {
display: grid;
grid-template-columns: 70px 220px 1fr;
gap: 16px;
align-items: center;
padding: 12px 18px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 4px;
font-size: 13px;
transition: border-color .2s;
}
.endpoint-row:hover { border-color: var(--muted); }
.method {
font-family: var(--mono);
font-size: 11px;
font-weight: 700;
padding: 3px 8px;
border-radius: 3px;
text-align: center;
letter-spacing: .04em;
}
.method-get { background: rgba(0,184,255,.12); color: var(--accent2); }
.method-post { background: rgba(0,255,136,.12); color: var(--accent); }
.path { font-family: var(--mono); font-size: 13px; color: #fff; }
.ep-desc { color: var(--muted); font-size: 13px; }
/* ─── code block ─── */
.code-wrap {
margin-top: 28px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.code-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 18px;
border-bottom: 1px solid var(--border);
}
.code-title { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.copy-btn {
font-family: var(--mono);
font-size: 10px;
color: var(--muted);
background: none;
border: 1px solid var(--border);
border-radius: 3px;
padding: 3px 10px;
cursor: pointer;
transition: all .2s;
}
.copy-btn:hover { color: var(--accent); border-color: var(--accent); }
pre {
margin: 0;
padding: 22px;
overflow-x: auto;
font-family: var(--mono);
font-size: 12px;
line-height: 1.75;
color: var(--text);
}
.kw { color: var(--accent2); }
.str { color: var(--accent); }
.cm { color: var(--muted); }
.pn { color: var(--warn); }
/* ─── data sources ─── */
.source-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
margin-top: 28px;
}
.source-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 22px;
display: flex;
gap: 16px;
align-items: flex-start;
}
.source-icon {
font-size: 22px;
flex-shrink: 0;
line-height: 1;
}
.source-name {
font-family: var(--mono);
font-size: 13px;
color: #fff;
margin-bottom: 4px;
}
.source-meta { font-size: 12px; color: var(--muted); }
/* ─── footer ─── */
footer {
border-top: 1px solid var(--border);
padding: 28px 0;
margin-top: 40px;
}
.footer-inner {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 14px;
}
.footer-left { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.footer-links { display: flex; gap: 20px; }
.footer-links a {
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
text-decoration: none;
transition: color .2s;
}
.footer-links a:hover { color: var(--accent); }
/* ─── responsive ─── */
@media (max-width: 600px) {
.endpoint-row { grid-template-columns: 60px 1fr; }
.ep-desc { display: none; }
.nav-links { display: none; }
}
/* ─── fade-in ─── */
.fade-in {
opacity: 0;
transform: translateY(18px);
animation: fadeUp .55s ease forwards;
}
@keyframes fadeUp {
to { opacity: 1; transform: none; }
}
.fade-in:nth-child(1) { animation-delay: .05s; }
.fade-in:nth-child(2) { animation-delay: .12s; }
.fade-in:nth-child(3) { animation-delay: .19s; }
.fade-in:nth-child(4) { animation-delay: .26s; }
</style>
</head>
<body>
<!-- ── HEADER ──────────────────────────────────────────────── -->
<header>
<div class="wrap">
<nav class="nav">
<div class="logo">
<span class="logo-dot"></span>
SC-AUDIT-ENV v1.2.0
</div>
<ul class="nav-links">
<li><a href="#tasks">Tasks</a></li>
<li><a href="#api">API</a></li>
<li><a href="#quickstart">Quickstart</a></li>
<li><a href="/docs">Swagger</a></li>
</ul>
</nav>
</div>
</header>
<!-- ── HERO ────────────────────────────────────────────────── -->
<div class="wrap">
<div class="hero">
<div class="hero-tag fade-in">OpenEnv Β· Reinforcement Learning Β· Smart Contract Security</div>
<h1 class="fade-in">Audit Solidity.<br><span>Train smarter agents.</span></h1>
<p class="hero-sub fade-in">
An OpenEnv-compliant RL environment for smart contract security analysis.
Agents explore real-world DeFi contracts from Certora audit reports β€”
detecting vulnerabilities, deriving properties, and checking invariants.
</p>
<div class="hero-badges fade-in">
<span class="badge badge-green">openenv</span>
<span class="badge badge-blue">solidity</span>
<span class="badge badge-blue">aave</span>
<span class="badge badge-blue">lido</span>
<span class="badge badge-yellow">certora</span>
<span class="badge badge-green">3 tasks</span>
</div>
<div class="cta-row fade-in">
<a class="btn btn-primary" href="/docs">Explore API Docs</a>
<a class="btn btn-outline" href="#quickstart">Quick Start β†’</a>
</div>
</div>
</div>
<!-- ── TASKS ───────────────────────────────────────────────── -->
<section id="tasks">
<div class="wrap">
<div class="section-label">// three tasks</div>
<h2>What can agents learn here?</h2>
<p style="color:var(--muted); max-width:520px; margin-top:6px;">
Each task mirrors a real step in a professional smart contract audit.
Difficulty is graded easy β†’ medium β†’ hard.
</p>
<div class="task-grid">
<!-- Task 1 -->
<div class="task-card medium">
<div class="task-header">
<span class="task-num">TASK 01</span>
<span class="diff-badge diff-medium">medium</span>
</div>
<h3>Targeted Vulnerability Detection</h3>
<p class="task-desc">
Given a Solidity file, identify the vulnerable function and name the
vulnerability class in 2–3 words. Submit <code>NO</code> if no vulnerability exists.
</p>
<div class="task-actions">
<span class="action-chip">list_functions</span>
<span class="action-chip">get_function_code</span>
<span class="action-chip">get_state_variable</span>
<span class="action-chip">get_call_graph</span>
<span class="action-chip">submit</span>
</div>
</div>
<!-- Task 2 -->
<div class="task-card hard">
<div class="task-header">
<span class="task-num">TASK 02</span>
<span class="diff-badge diff-hard">hard</span>
</div>
<h3>Property Discovery</h3>
<p class="task-desc">
Given a single function, derive its natural-language safety property β€”
the kind a formal verification engineer would write as a CVL invariant.
One submission attempt per episode, scored 0–5.
</p>
<div class="task-actions">
<span class="action-chip">get_function_natspec</span>
<span class="action-chip">get_related_functions</span>
<span class="action-chip">get_similar_rule</span>
<span class="action-chip">submit_property</span>
</div>
</div>
<!-- Task 3 -->
<div class="task-card easy">
<div class="task-header">
<span class="task-num">TASK 03</span>
<span class="diff-badge diff-easy">easy</span>
</div>
<h3>Rule Checker</h3>
<p class="task-desc">
A property is given in plain English. At least one function in the
contract breaks it. Find the offender. Partial credit for naming a
direct caller of the true violating function.
</p>
<div class="task-actions">
<span class="action-chip">list_functions</span>
<span class="action-chip">get_function_code</span>
<span class="action-chip">get_property_specification</span>
<span class="action-chip">submit_function</span>
</div>
</div>
</div>
</div>
</section>
<!-- ── DATA SOURCES ────────────────────────────────────────── -->
<section>
<div class="wrap">
<div class="section-label">// dataset</div>
<h2>Grounded in real audits</h2>
<p style="color:var(--muted); max-width:520px; margin-top:6px;">
All contracts and vulnerabilities are sourced from public Certora audit
reports on production DeFi protocols.
</p>
<div class="source-grid">
<div class="source-card">
<div class="source-icon">🏦</div>
<div>
<div class="source-name">AaveVault</div>
<div class="source-meta">Certora Audit Β· ERC-4626 vault Β· Access control, share accounting</div>
</div>
</div>
<div class="source-card">
<div class="source-icon">🏦</div>
<div>
<div class="source-name">AaveVaultV2</div>
<div class="source-meta">Certora Audit Β· Upgraded vault Β· Fee logic, reentrancy surfaces</div>
</div>
</div>
<div class="source-card">
<div class="source-icon">πŸ”·</div>
<div>
<div class="source-name">Lido Finance</div>
<div class="source-meta">Certora Audit Β· Liquid staking Β· Rebasing token invariants</div>
</div>
</div>
</div>
</div>
</section>
<!-- ── API REFERENCE ───────────────────────────────────────── -->
<section id="api">
<div class="wrap">
<div class="section-label">// http interface</div>
<h2>API Endpoints</h2>
<div class="endpoint-list">
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/health</span>
<span class="ep-desc">Liveness probe</span>
</div>
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/tasks</span>
<span class="ep-desc">List all tasks with difficulty and status</span>
</div>
<div class="endpoint-row">
<span class="method method-post">POST</span>
<span class="path">/reset</span>
<span class="ep-desc">Start a new episode &mdash; body: <code>{task_id, seed}</code></span>
</div>
<div class="endpoint-row">
<span class="method method-post">POST</span>
<span class="path">/step</span>
<span class="ep-desc">Take one action &mdash; body: <code>{action_type, params}</code></span>
</div>
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/state</span>
<span class="ep-desc">Full internal episode state (debug)</span>
</div>
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/action_space</span>
<span class="ep-desc">Valid actions for a task &mdash; query: <code>?task_id=</code></span>
</div>
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/observation_space</span>
<span class="ep-desc">Observation schema (all tasks share this)</span>
</div>
<div class="endpoint-row">
<span class="method method-get">GET</span>
<span class="path">/docs</span>
<span class="ep-desc">Interactive Swagger UI</span>
</div>
</div>
</div>
</section>
<!-- ── QUICK START ─────────────────────────────────────────── -->
<section id="quickstart">
<div class="wrap">
<div class="section-label">// quick start</div>
<h2>Run an episode in 3 calls</h2>
<div class="code-wrap">
<div class="code-header">
<span class="code-title">bash</span>
<button class="copy-btn" onclick="copyCode(this)">copy</button>
</div>
<pre id="snippet"><span class="cm"># 1. Reset β€” start Task 1 with a fixed seed</span>
<span class="kw">curl</span> -X POST $SPACE_URL/reset \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{"task_id": "task1_vuln_detection", "seed": 42}'</span>
<span class="cm"># 2. Explore β€” list all functions in the contract</span>
<span class="kw">curl</span> -X POST $SPACE_URL/step \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{"action_type": "list_functions", "params": {}}'</span>
<span class="cm"># 3. Submit β€” name the vulnerable function</span>
<span class="kw">curl</span> -X POST $SPACE_URL/step \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{"action_type": "submit", "params": {"function_name": "withdraw", "vulnerability_type": "reentrancy"}}'</span></pre>
</div>
</div>
</section>
<!-- ── FOOTER ──────────────────────────────────────────────── -->
<footer>
<div class="wrap">
<div class="footer-inner">
<div class="footer-left">
SC-AUDIT-ENV &nbsp;Β·&nbsp; MIT License &nbsp;Β·&nbsp; Data from Certora public audits
</div>
<div class="footer-links">
<a href="/docs">Swagger</a>
<a href="/tasks">Tasks JSON</a>
<a href="/health">Health</a>
</div>
</div>
</div>
</footer>
<script>
function copyCode(btn) {
const pre = document.getElementById('snippet');
const text = pre.innerText;
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'copied!';
setTimeout(() => btn.textContent = 'copy', 2000);
});
}
</script>
</body>
</html>