Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions services/operator-dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
</head>
<body><main class="frame">
<header><div><div class="eyebrow">Corner Store / local console</div><h1>Compliance, visible<br>at settlement.</h1><p class="subtitle">Read deployment state in Operator view. In RFQ demo, click through a signed quote, current-policy check and protected Router settlement.</p></div><div class="mode"><button class="mode-button active" data-mode="operator" type="button">Operator view</button><button class="mode-button" data-mode="demo" type="button">RFQ demo</button></div></header>
<div id="operatorMode"><div class="trace"><div><b>PROFILE</b><span id="asset">Loading…</span></div><div><b>VENUES</b><span id="venues">Loading…</span></div><div><b>CONTROL PLANE</b><span>Read-only snapshot</span></div><div><b>CHANGE PATH</b><span>External multisig proposal</span></div></div><section><h2>Indexed events</h2><button id="refresh" class="alt" type="button">Refresh snapshot</button><pre id="events">Loading…</pre><p class="muted">No private keys. No direct transaction endpoint.</p></section></div>
<div id="operatorMode"><div class="trace"><div><b>PROFILE</b><span id="asset">Loading…</span></div><div><b>NETWORK</b><span id="network">Loading…</span></div><div><b>VENUES</b><span id="venues">Loading…</span></div><div><b>GOVERNANCE</b><span id="governance">Loading…</span></div></div><div class="grid"><section><h2>Active deployment</h2><div class="facts"><div><span>RWA token</span><strong id="rwaToken">Loading…</strong></div><div><span>Execution Router</span><strong id="router">Loading…</strong></div><div><span>RFQ adapter</span><strong id="rfqAdapter">Loading…</strong></div><div><span>AMM adapter</span><strong id="ammAdapter">Loading…</strong></div><div><span>AMM pool</span><strong id="pool">Loading…</strong></div><div><span>Change path</span><strong id="changePath">External multisig</strong></div></div><p class="muted">Addresses come from the selected deployment artifact. This view is read-only.</p></section><section><h2>Indexed events</h2><button id="refresh" class="alt" type="button">Refresh snapshot</button><pre id="events">Loading…</pre><p class="muted">No private keys. No direct transaction endpoint.</p></section></div></div>
<div id="demoMode" class="hidden"><div id="demoTrace" class="trace"><div data-stage="Mock TA profile"><b>01 / PROFILE</b><span>Mock TA-seeded BUIDL-like investor</span></div><div data-stage="RFQ quote"><b>02 / QUOTE</b><span>Backend prices and EIP-712 signs</span></div><div data-stage="ComplianceEngine"><b>03 / GATE</b><span>Router checks current policy</span></div><div data-stage="RFQ settlement"><b>04 / SETTLE</b><span>RFQAdapter transfers ERC-3643</span></div></div><div class="grid"><section><h2>Run the RFQ flow</h2><label for="backend">Local RFQ backend</label><input id="backend" value="http://127.0.0.1:8787"><label for="amount">BUIDL-like units</label><input id="amount" value="5000000" inputmode="decimal"><div class="button-row"><button id="connect" class="alt" type="button">Check backend</button><button id="settle" type="button">Run compliant RFQ trade</button><button id="revoke" class="danger" type="button">Revoke maker &amp; retry</button></div><div id="status" class="status" role="status">Start the local RFQ demo runner, then check the backend connection.</div><pre id="quoteOutput" class="hidden"></pre></section><section><h2>What this proves</h2><div class="facts"><div><span>Trusted source</span><strong>Mock TA fixture</strong></div><div><span>Demo investor</span><strong>Canonical local Anvil account</strong></div><div><span>Quote signer</span><strong id="maker">Not connected</strong></div><div><span>Compliance decision</span><strong>On-chain at fill time</strong></div><div><span>Execution path</span><strong>Router → RFQAdapter</strong></div></div><p class="muted">The browser receives no private key. The local demo backend can settle only on Anvil with fixed demo accounts; the Router remains the final compliance gate.</p></section></div></div>
</main><script>
const $=id=>document.getElementById(id);
function selectMode(mode){$('operatorMode').classList.toggle('hidden',mode!=='operator');$('demoMode').classList.toggle('hidden',mode!=='demo');document.querySelectorAll('[data-mode]').forEach(b=>b.classList.toggle('active',b.dataset.mode===mode))}document.querySelectorAll('[data-mode]').forEach(b=>b.addEventListener('click',()=>selectMode(b.dataset.mode)));
async function get(path){const r=await fetch(path);if(!r.ok)throw new Error(await r.text());return r.json()}async function refresh(){try{const [config,events]=await Promise.all([get('/api/v1/config'),get('/api/v1/events')]);$('asset').textContent=config.asset.profile;$('venues').textContent=Object.entries(config.venues).filter(([,v])=>v).map(([n])=>n).join(', ');$('events').textContent=JSON.stringify(events.events,null,2)}catch(e){$('events').textContent=String(e)}}$('refresh').addEventListener('click',refresh);refresh();
async function get(path){const r=await fetch(path);if(!r.ok)throw new Error(await r.text());return r.json()}function shortAddress(value){if(typeof value!=='string'||!/^0x[a-f0-9]{40}$/i.test(value))return value||'Not configured';return `${value.slice(0,6)}…${value.slice(-4)}`}function show(id,value){$(id).textContent=value||'Not configured'}async function refresh(){try{const [config,deployment,events]=await Promise.all([get('/api/v1/config'),get('/api/v1/deployment'),get('/api/v1/events')]);show('asset',config.asset.profile);show('network',config.deployment.network);show('venues',Object.entries(config.venues).filter(([,v])=>v).map(([n])=>n).join(', ')||'None enabled');show('governance',`${config.governance.requiredApprovals} approvals`);show('rwaToken',shortAddress(deployment.rwaToken));show('router',shortAddress(deployment.router));show('rfqAdapter',shortAddress(deployment.rfqAdapter));show('ammAdapter',shortAddress(deployment.ammAdapter));show('pool',shortAddress(deployment.pool));$('events').textContent=JSON.stringify(events.events,null,2)}catch(e){$('events').textContent=String(e)}}$('refresh').addEventListener('click',refresh);refresh();
const endpoint=path=>`${$('backend').value.replace(/\/$/,'')}${path}`;function status(message,kind=''){$('status').textContent=message;$('status').className=`status ${kind}`}function toBaseUnits(value){if(!/^\d+(?:\.\d{1,18})?$/.test(value))throw new Error('Enter a positive amount with up to 18 decimals.');const [whole,fraction='']=value.split('.');const out=`${whole}${fraction.padEnd(18,'0')}`.replace(/^0+/,'')||'0';if(BigInt(out)<=0n)throw new Error('Amount must be greater than zero.');return out}async function demoRequest(path,init){const r=await fetch(endpoint(path),init);const body=await r.json();if(!r.ok)throw new Error(body.message||body.error||'Backend request failed.');return body}
async function connect(){try{status('Checking local RFQ backend…');const health=await demoRequest('/health');$('maker').textContent=`${health.maker.slice(0,8)}…${health.maker.slice(-6)}`;status(health.demoSettlementEnabled?'Connected: click-through settlement is ready.':'Connected, but start the e2e runner with demo settlement enabled.','good')}catch(e){status(String(e.message||e),'bad')}}
function resetTrace(){document.querySelectorAll('#demoTrace div').forEach(item=>item.classList.remove('passed','rejected'))}function renderTrade(result){resetTrace();result.trace.forEach(step=>{const item=document.querySelector(`#demoTrace [data-stage="${step.stage}"]`);if(item){item.classList.add(step.status);item.querySelector('span').textContent=step.detail}});$('quoteOutput').textContent=JSON.stringify(result,null,2);$('quoteOutput').classList.remove('hidden');if(result.transaction)status(`Settled in block ${result.transaction.blockNumber}. BUIDL-like balance changed by +${Number(result.transaction.rwaDelta)/1e18}.`,'good');else status(`Rejected before settlement: ${result.rejection||'current maker policy failed'}.`,'bad')}
Expand Down
2 changes: 1 addition & 1 deletion services/operator-dashboard/test/smoke.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const fs = require("fs");
const path = require("path");

const html = fs.readFileSync(path.join(__dirname, "..", "index.html"), "utf8");
for (const marker of ["/api/v1/config", "/api/v1/events", "No private keys", "multisig", "RFQ demo", "Run compliant RFQ trade", "Revoke maker &amp; retry", "/demo/trade", "Router → RFQAdapter"]) {
for (const marker of ["/api/v1/config", "/api/v1/deployment", "/api/v1/events", "Active deployment", "No private keys", "multisig", "RFQ demo", "Run compliant RFQ trade", "Revoke maker &amp; retry", "/demo/trade", "Router → RFQAdapter"]) {
if (!html.includes(marker)) throw new Error(`dashboard safety/data marker missing: ${marker}`);
}
const server = fs.readFileSync(path.join(__dirname, "..", "server.js"), "utf8");
Expand Down
Loading