anycoder-bd0ca9d1 / index.html
idgmatrix's picture
Upload folder using huggingface_hub
1bbcca8 verified
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced DEX AMM Visualizer | AnyCoder</title>
<!-- External Libraries -->
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #0f172a;
--card-bg: #1e293b;
--accent-primary: #6366f1;
--accent-curve: #f59e0b;
--accent-fixed: #10b981;
--text-primary: #f8fafc;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
overflow-x: hidden;
}
/* Glassmorphism */
.glass-card {
background: rgba(30, 41, 59, 0.7);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 16px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
}
/* Inputs */
.custom-input {
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(148, 163, 184, 0.2);
color: var(--text-primary);
transition: all 0.3s ease;
}
.custom-input:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
/* Tab System */
.tab-btn {
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.tab-btn.active {
background: rgba(255, 255, 255, 0.1);
border-bottom: 2px solid;
}
.tab-btn.active[data-model="cp"] { border-color: var(--accent-primary); color: #818cf8; }
.tab-btn.active[data-model="ss"] { border-color: var(--accent-curve); color: #fbbf24; }
.tab-btn.active[data-model="cs"] { border-color: var(--accent-fixed); color: #34d399; }
/* Range Slider */
input[type=range] {
-webkit-appearance: none;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 18px;
width: 18px;
border-radius: 50%;
background: #fff;
cursor: pointer;
margin-top: -7px;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: #334155;
border-radius: 2px;
}
.font-mono { font-family: 'JetBrains Mono', monospace; }
/* Math Formula Rendering */
.katex-display {
font-size: 0.9em;
overflow-x: auto;
padding: 0.5rem;
}
</style>
</head>
<body class="min-h-screen flex flex-col">
<!-- Header -->
<header class="w-full py-4 px-6 border-b border-slate-800 flex justify-between items-center bg-slate-900/80 backdrop-blur-md sticky top-0 z-50">
<div class="flex items-center gap-3">
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-blue-600 to-purple-600 flex items-center justify-center text-white font-bold text-xl shadow-lg shadow-blue-500/20">
<i class="fa-solid fa-layer-group"></i>
</div>
<div>
<h1 class="text-xl font-bold tracking-tight leading-none">Multi-Model <span class="text-blue-400">AMM</span></h1>
<p class="text-xs text-slate-500 font-mono">DEX Algorithm Simulator</p>
</div>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-sm font-medium text-slate-400 hover:text-white transition-colors flex items-center gap-2 border border-slate-700 px-3 py-1.5 rounded-full hover:bg-slate-800">
Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square text-xs"></i>
</a>
</header>
<!-- Main Content -->
<main class="flex-grow container mx-auto px-4 py-8 grid grid-cols-1 lg:grid-cols-12 gap-6">
<!-- Left Column: Controls -->
<div class="lg:col-span-4 flex flex-col gap-6">
<!-- Model Selection Tabs -->
<div class="glass-card p-2 flex justify-between gap-1">
<button class="tab-btn active flex-1 py-2 px-3 rounded-lg text-sm font-semibold text-slate-400 hover:text-white" onclick="switchModel('cp')" data-model="cp">
<i class="fa-solid fa-shuffle mr-1"></i> Uniswap
</button>
<button class="tab-btn flex-1 py-2 px-3 rounded-lg text-sm font-semibold text-slate-400 hover:text-white" onclick="switchModel('ss')" data-model="ss">
<i class="fa-solid fa-bezier-curve mr-1"></i> Curve
</button>
<button class="tab-btn flex-1 py-2 px-3 rounded-lg text-sm font-semibold text-slate-400 hover:text-white" onclick="switchModel('cs')" data-model="cs">
<i class="fa-solid fa-equals mr-1"></i> Fixed
</button>
</div>
<!-- Pool Configuration -->
<section class="glass-card p-6 relative overflow-hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold flex items-center gap-2">
<i class="fa-solid fa-coins text-blue-400"></i> Liquidity Pool
</h2>
<button onclick="resetPool()" class="text-xs text-slate-500 hover:text-white underline">Reset 1:1</button>
</div>
<div class="space-y-4">
<div>
<label class="flex justify-between text-xs uppercase font-bold text-slate-500 mb-1">
<span>Reserve X (Token A)</span>
<span class="text-slate-600">Input Token</span>
</label>
<div class="flex items-center gap-2">
<span class="text-xl w-8 text-center">🍏</span>
<input type="number" id="reserveA" value="1000" class="custom-input w-full p-3 rounded-lg font-mono text-lg" oninput="updateSimulation()">
</div>
</div>
<div>
<label class="flex justify-between text-xs uppercase font-bold text-slate-500 mb-1">
<span>Reserve Y (Token B)</span>
<span class="text-slate-600">Output Token</span>
</label>
<div class="flex items-center gap-2">
<span class="text-xl w-8 text-center">🫐</span>
<input type="number" id="reserveB" value="1000" class="custom-input w-full p-3 rounded-lg font-mono text-lg" oninput="updateSimulation()">
</div>
</div>
<!-- Curve Specific Input: Amp Factor -->
<div id="ampContainer" class="hidden pt-2 border-t border-slate-700 mt-2">
<div class="flex justify-between mb-1">
<label class="text-xs uppercase font-bold text-amber-500">Amplification (A)</label>
<span id="ampValue" class="text-xs font-mono text-white">100</span>
</div>
<input type="range" id="ampSlider" min="1" max="1000" value="100" class="w-full" oninput="updateSimulation()">
<p class="text-[10px] text-slate-500 mt-1">Higher A = Flatter curve (closer to fixed price).</p>
</div>
</div>
</section>
<!-- Swap Interface -->
<section class="glass-card p-6 border-t-4 border-t-blue-500 transition-colors" id="swapCard">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<i class="fa-solid fa-right-left"></i> Swap Simulator
</h2>
<div class="relative">
<!-- Input -->
<div class="bg-slate-800/50 p-4 rounded-xl border border-slate-700 focus-within:border-blue-500/50 transition-colors">
<div class="flex justify-between mb-2">
<label class="text-xs text-slate-400">Pay (In)</label>
<span class="text-xs text-slate-500 font-mono">Token A</span>
</div>
<div class="flex items-center gap-3">
<input type="number" id="swapInput" value="0" class="bg-transparent text-2xl font-bold w-full outline-none text-white placeholder-slate-600 font-mono" placeholder="0.0">
</div>
</div>
<!-- Arrow -->
<div class="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
<div class="bg-slate-800 border border-slate-700 p-1.5 rounded-full shadow-xl text-slate-400">
<i class="fa-solid fa-arrow-down text-sm"></i>
</div>
</div>
<!-- Output -->
<div class="bg-slate-800/50 p-4 rounded-xl border border-slate-700 mt-2">
<div class="flex justify-between mb-2">
<label class="text-xs text-slate-400">Receive (Out)</label>
<span class="text-xs text-slate-500 font-mono">Token B</span>
</div>
<div class="flex items-center gap-3">
<input type="text" id="swapOutput" readonly class="bg-transparent text-2xl font-bold w-full outline-none text-emerald-400 font-mono" value="0.00">
</div>
</div>
</div>
<div class="mt-4">
<input type="range" id="swapSlider" min="0" max="1000" value="0" class="w-full accent-blue-500" oninput="syncSlider()">
</div>
</section>
<!-- Stats Mini -->
<div class="glass-card p-4 flex justify-between items-center">
<div>
<div class="text-xs text-slate-500">Slippage / Impact</div>
<div id="priceImpact" class="font-bold text-emerald-400 font-mono">0.00%</div>
</div>
<div class="text-right">
<div class="text-xs text-slate-500">Execution Price</div>
<div id="execPrice" class="font-bold text-white font-mono">0.00</div>
</div>
</div>
</div>
<!-- Right Column: Visualization -->
<div class="lg:col-span-8 flex flex-col gap-6">
<!-- Chart -->
<section class="glass-card p-6 h-[450px] flex flex-col relative">
<div class="flex justify-between items-center mb-2">
<div>
<h2 class="text-lg font-semibold" id="chartTitle">Constant Product Curve</h2>
<p class="text-xs text-slate-400" id="chartSubtitle">x * y = k</p>
</div>
<div class="flex gap-2 text-xs">
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-indigo-500"></span> Curve</span>
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-emerald-500"></span> Current</span>
<span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-pink-500"></span> New</span>
</div>
</div>
<div class="flex-grow relative w-full h-full">
<canvas id="ammChart"></canvas>
</div>
</section>
<!-- Formula Explanation -->
<section class="glass-card p-6">
<h3 class="text-sm font-bold text-slate-400 uppercase mb-4 tracking-wider flex items-center gap-2">
<i class="fa-solid fa-square-root-variable"></i> Mathematical Model
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Formula Display -->
<div class="bg-slate-900/50 p-4 rounded-lg border border-slate-700/50 flex flex-col justify-center items-center text-center min-h-[100px]">
<div id="formulaDisplay" class="font-mono text-lg text-blue-300">
x · y = k
</div>
<div id="formulaDesc" class="text-xs text-slate-500 mt-2">
Uniswap V2 Model. Ensures liquidity is never depleted.
</div>
</div>
<!-- Dynamic Variables -->
<div class="space-y-2 text-sm font-mono">
<div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
<span class="text-slate-400">Invariant (K/D)</span>
<span id="mathK" class="text-indigo-300">1,000,000</span>
</div>
<div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
<span class="text-slate-400">Current Spot Price</span>
<span id="spotPrice" class="text-emerald-400">1.0000</span>
</div>
<div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
<span class="text-slate-400">Effective Output (dy)</span>
<span id="mathDy" class="text-white">0.00</span>
</div>
</div>
</div>
</section>
</div>
</main>
<script>
// --- State Management ---
const state = {
model: 'cp', // 'cp' (Constant Product), 'ss' (StableSwap), 'cs' (Constant Sum)
resA: 1000,
resB: 1000,
amp: 100, // For Curve
input: 0
};
let chartInstance = null;
// --- DOM Elements ---
const els = {
resA: document.getElementById('reserveA'),
resB: document.getElementById('reserveB'),
inp: document.getElementById('swapInput'),
out: document.getElementById('swapOutput'),
slider: document.getElementById('swapSlider'),
ampContainer: document.getElementById('ampContainer'),
ampSlider: document.getElementById('ampSlider'),
ampValue: document.getElementById('ampValue'),
chartTitle: document.getElementById('chartTitle'),
chartSubtitle: document.getElementById('chartSubtitle'),
formulaDisplay: document.getElementById('formulaDisplay'),
formulaDesc: document.getElementById('formulaDesc'),
mathK: document.getElementById('mathK'),
spotPrice: document.getElementById('spotPrice'),
mathDy: document.getElementById('mathDy'),
impact: document.getElementById('priceImpact'),
execPrice: document.getElementById('execPrice'),
tabs: document.querySelectorAll('.tab-btn'),
swapCard: document.getElementById('swapCard')
};
// --- Initialization ---
window.onload = () => {
initChart();
updateSimulation();
els.inp.addEventListener('input', () => {
els.slider.value = Math.min(els.inp.value, 1000);
updateSimulation();
});
};
// --- Core Logic ---
function switchModel(model) {
state.model = model;
// Update Tabs UI
els.tabs.forEach(t => {
if(t.dataset.model === model) t.classList.add('active');
else t.classList.remove('active');
});
// Update Controls UI
if (model === 'ss') {
els.ampContainer.classList.remove('hidden');
els.chartTitle.innerText = "Stableswap Curve";
els.chartSubtitle.innerText = "An^n * sum(x) + D = ADn^n + D^(n+1) / (n^n * prod(x))";
els.formulaDisplay.innerHTML = "An^n \\sum x_i + D = ADn^n + \\frac{D^{n+1}}{n^n \\prod x_i}";
els.formulaDesc.innerText = "Curve Finance Model. Low slippage for like-assets (e.g., USDC/DAI).";
els.swapCard.style.borderColor = "#f59e0b";
} else if (model === 'cs') {
els.ampContainer.classList.add('hidden');
els.chartTitle.innerText = "Constant Sum";
els.chartSubtitle.innerText = "x + y = k";
els.formulaDisplay.innerText = "x + y = k";
els.formulaDesc.innerText = "Fixed Exchange Rate. Zero slippage until empty.";
els.swapCard.style.borderColor = "#10b981";
} else {
els.ampContainer.classList.add('hidden');
els.chartTitle.innerText = "Constant Product";
els.chartSubtitle.innerText = "x * y = k";
els.formulaDisplay.innerText = "x · y = k";
els.formulaDesc.innerText = "Uniswap V2 Model. Infinite liquidity range.";
els.swapCard.style.borderColor = "#6366f1";
}
updateSimulation();
}
function resetPool() {
els.resA.value = 1000;
els.resB.value = 1000;
updateSimulation();
}
function syncSlider() {
els.inp.value = els.slider.value;
updateSimulation();
}
function updateSimulation() {
// Update State
state.resA = parseFloat(els.resA.value) || 1;
state.resB = parseFloat(els.resB.value) || 1;
state.input = parseFloat(els.inp.value) || 0;
state.amp = parseInt(els.ampSlider.value);
els.ampValue.innerText = state.amp;
let output = 0;
let newY = 0;
let invariant = 0;
let spotP = 0;
// --- MATH LOGIC ---
if (state.model === 'cp') {
// x * y = k
const k = state.resA * state.resB;
invariant = k;
const newX = state.resA + state.input;
newY = k / newX;
output = state.resB - newY;
spotP = state.resB / state.resA;
} else if (state.model === 'cs') {
// x + y = k
const k = state.resA + state.resB;
invariant = k;
const newX = state.resA + state.input;
// If newX > k, pool is empty
if (newX >= k) {
output = state.resB; // Can only take what's left
newY = 0;
} else {
newY = k - newX;
output = state.resB - newY;
}
spotP = 1.0;
} else if (state.model === 'ss') {
// Stableswap Math (Simplified for 2 coins)
// D is invariant
const D = getD(state.resA, state.resB, state.amp);
invariant = D;
const newX = state.resA + state.input;
newY = getY(newX, D, state.amp);
output = state.resB - newY;
// Approx spot price (derivative at current point)
// For display, simple ratio is close enough for small dx, but let's use small delta
const smallDy = state.resB - getY(state.resA + 0.1, D, state.amp);
spotP = smallDy / 0.1;
}
// --- UI Updates ---
els.out.value = formatNum(output, 4);
els.mathDy.innerText = formatNum(output, 2);
els.mathK.innerText = formatNum(invariant, 0);
els.spotPrice.innerText = formatNum(spotP, 4);
// Execution Price & Impact
const execP = state.input > 0 ? output / state.input : 0;
els.execPrice.innerText = state.input > 0 ? formatNum(execP, 4) : "0.00";
let impact = 0;
if (state.input > 0 && spotP > 0) {
impact = ((spotP - execP) / spotP) * 100;
}
els.impact.innerText = `${formatNum(impact, 2)}%`;
// Color coding impact
if(impact < 0.1) els.impact.className = "font-bold text-emerald-400 font-mono";
else if(impact < 1.0) els.impact.className = "font-bold text-blue-400 font-mono";
else if(impact < 5.0) els.impact.className = "font-bold text-yellow-400 font-mono";
else els.impact.className = "font-bold text-red-500 font-mono";
// Update Chart
updateChart(state.resA, state.resB, state.resA + state.input, newY, invariant);
}
// --- Curve (Stableswap) Math Helpers ---
// Based on Curve Whitepaper for n=2
function getD(xp, yp, A) {
// Newton's method to find D
const S = xp + yp;
if (S === 0) return 0;
let D = S;
const Ann = A * 4; // n=2, so Ann = A * 2^2 = 4A
for (let i = 0; i < 15; i++) {
let D_P = D * D * D / (4 * xp * yp); // D^(n+1) / (n^n * prod(x)) -> D^3 / (4xy)
let prevD = D;
// Formula: D = (Ann * S + D_P * n) * D / ((Ann - 1) * D + (n + 1) * D_P)
// n=2
let num = (Ann * S + 2 * D_P) * D;
let den = (Ann - 1) * D + 3 * D_P;
D = num / den;
if (Math.abs(D - prevD) <= 1) break;
}
return D;
}
function getY(x, D, A) {
// Calculate Y given X and D
const Ann = A * 4;
const c = (D * D * D) / (4 * x * Ann); // c = D^(n+1) / (n * prod(x_others) * Ann * n^n)
const b = x + (D / Ann) - D; // b = sum(x_others) + D/Ann - D
// Solve y^2 + b*y = c
// y = ( -b + sqrt(b^2 + 4c) ) / 2 (Standard quadratic formula, since y must be positive)
// Or iterative (Curve uses iterative for gas efficiency, we can use quadratic here for JS speed for n=2)
// y^2 + (x + D/4A - D)y - D^3/(16Ax) = 0
// Let's trust the iterative method to match Curve logic exactly
let y = D;
for(let i=0; i < 15; i++){
// y = (y^2 + c) / (2y + b)
let prevY = y;
y = (y*y + c) / (2*y + b);
if(Math.abs(y - prevY) <= 1) break;
}
return y;
}
// --- Charting ---
function initChart() {
const ctx = document.getElementById('ammChart').getContext('2d');
Chart.defaults.color = '#64748b';
Chart.defaults.font.family = "'Inter', sans-serif";
chartInstance = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Curve',
data: [],
borderColor: '#6366f1', // Will change dynamically
borderWidth: 2,
pointRadius: 0,
tension: 0.4
},
{
label: 'Current',
data: [],
backgroundColor: '#10b981',
borderColor: '#fff',
borderWidth: 2,
pointRadius: 6
},
{
label: 'New',
data: [],
backgroundColor: '#ec4899',
borderColor: '#fff',
borderWidth: 2,
pointRadius: 6
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { display: false },
tooltip: {
mode: 'index',
intersect: false,
backgroundColor: 'rgba(15, 23, 42, 0.9)',
titleColor: '#fff',
bodyFont: { family: 'JetBrains Mono' }
}
},
scales: {
x: {
type: 'linear',
title: { display: true, text: 'Reserve A (Input)', color: '#94a3b8' },
grid: { color: 'rgba(255, 255, 255, 0.05)' }
},
y: {
title: { display: true, text: 'Reserve B (Output)', color: '#94a3b8' },
grid: { color: 'rgba(255, 255, 255, 0.05)' }
}
}
}
});
}
function updateChart(currX, currY, newX, newY, invariant) {
const dataPoints = [];
const steps = 100;
// Calculate range to draw
const centerX = currX;
const range = Math.max(currX, newX) * 1.5;
const startX = Math.max(1, centerX - range/2);
const endX = centerX + range/2;
// Update Line Color based on model
if(state.model === 'cp') chartInstance.data.datasets[0].borderColor = '#6366f1';
if(state.model === 'ss') chartInstance.data.datasets[0].borderColor = '#f59e0b';
if(state.model === 'cs') chartInstance.data.datasets[0].borderColor = '#10b981';
for (let i = 0; i <= steps; i++) {
const xVal = startX + (endX - startX) * (i / steps);
let yVal = 0;
if (state.model === 'cp') {
yVal = invariant / xVal;
} else if (state.model === 'cs') {
yVal = invariant - xVal;
} else if (state.model === 'ss') {
yVal = getY(xVal, invariant, state.amp);
}
if (yVal >= 0) {
dataPoints.push({ x: xVal, y: yVal });
}
}
chartInstance.data.datasets[0].data = dataPoints;
chartInstance.data.datasets[1].data = [{ x: currX, y: currY }];
if (Math.abs(newX - currX) > 0.01) {
chartInstance.data.datasets[2].data = [{ x: newX, y: newY }];
} else {
chartInstance.data.datasets[2].data = [];
}
chartInstance.update('none');
}
function formatNum(num, decimals = 2) {
return parseFloat(num).toLocaleString('en-US', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
});
}
</script>
</body>
</html>