anycoder-2528d30e / index.html
idgmatrix's picture
Upload folder using huggingface_hub
830f840 verified
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DEX AMM Visualizer | Crypto Math</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.0.0/css/all.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color: #0f172a;
--card-bg: #1e293b;
--accent-primary: #6366f1;
--accent-secondary: #ec4899;
--text-primary: #f8fafc;
--text-secondary: #94a3b8;
--success: #10b981;
--danger: #ef4444;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
overflow-x: hidden;
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-color);
}
::-webkit-scrollbar-thumb {
background: var(--card-bg);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--accent-primary);
}
/* Glassmorphism Cards */
.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: transform 0.3s ease, box-shadow 0.3s ease;
}
.glass-card:hover {
box-shadow: 0 10px 40px rgba(99, 102, 241, 0.1);
}
/* 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);
}
/* Range Slider */
input[type=range] {
-webkit-appearance: none;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: var(--accent-primary);
cursor: pointer;
margin-top: -8px;
box-shadow: 0 0 10px rgba(99, 102, 241, 0.5);
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 4px;
cursor: pointer;
background: var(--card-bg);
border-radius: 2px;
}
/* Animations */
@keyframes pulse-glow {
0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4); }
70% { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); }
100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); }
}
.animate-swap {
animation: pulse-glow 1s infinite;
}
.formula-text {
font-family: 'Courier New', monospace;
}
.gradient-text {
background: linear-gradient(135deg, #6366f1 0%, #ec4899 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</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-indigo-500 to-pink-500 flex items-center justify-center text-white font-bold text-xl">
<i class="fa-solid fa-shuffle"></i>
</div>
<h1 class="text-xl font-bold tracking-tight">DEX <span class="font-light text-slate-400">Simulator</span></h1>
</div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-sm font-medium text-slate-400 hover:text-indigo-400 transition-colors flex items-center gap-2">
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-8">
<!-- Left Column: Controls & Swap -->
<div class="lg:col-span-4 flex flex-col gap-6">
<!-- Pool Configuration -->
<section class="glass-card p-6 relative overflow-hidden group">
<div class="absolute top-0 right-0 w-32 h-32 bg-indigo-500/10 rounded-full blur-3xl -mr-16 -mt-16 pointer-events-none"></div>
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<i class="fa-solid fa-database text-indigo-400"></i> 유동성 풀 설정 (Liquidity Pool)
</h2>
<div class="space-y-4">
<div>
<label class="block text-xs uppercase font-bold text-slate-500 mb-1 tracking-wider">Token A (Reserve X)</label>
<div class="flex items-center gap-2">
<span class="text-2xl">🍎</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="block text-xs uppercase font-bold text-slate-500 mb-1 tracking-wider">Token B (Reserve Y)</label>
<div class="flex items-center gap-2">
<span class="text-2xl">🍌</span>
<input type="number" id="reserveB" value="4000" class="custom-input w-full p-3 rounded-lg font-mono text-lg" oninput="updateSimulation()">
</div>
</div>
</div>
<div class="mt-6 p-4 bg-slate-900/50 rounded-lg border border-slate-700/50">
<div class="flex justify-between text-sm mb-1">
<span class="text-slate-400">Constant Product (k):</span>
<span id="constantK" class="font-mono text-indigo-300">4,000,000</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-slate-400">Current Price (A/B):</span>
<span id="spotPrice" class="font-mono text-emerald-400">1 A = 4.00 B</span>
</div>
</div>
</section>
<!-- Swap Interface -->
<section class="glass-card p-6 border-t-4 border-t-pink-500">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<i class="fa-solid fa-right-left text-pink-500"></i> 토큰 교환 (Swap)
</h2>
<div class="relative">
<div class="bg-slate-800/50 p-4 rounded-xl border border-slate-700 transition-colors focus-within:border-pink-500/50">
<div class="flex justify-between mb-2">
<label class="text-xs text-slate-400">지불할 금액 (You Pay)</label>
<span class="text-xs text-slate-500">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" placeholder="0.0">
<div class="bg-slate-700 px-2 py-1 rounded text-sm font-bold flex items-center gap-1">
🍎 A
</div>
</div>
</div>
<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-2 rounded-full shadow-xl text-slate-400">
<i class="fa-solid fa-arrow-down"></i>
</div>
</div>
<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">받을 금액 (You Receive)</label>
<span class="text-xs text-slate-500">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" value="0.00">
<div class="bg-slate-700 px-2 py-1 rounded text-sm font-bold flex items-center gap-1">
🍌 B
</div>
</div>
</div>
</div>
<div class="mt-4 space-y-2">
<label class="text-xs text-slate-400">빠른 입력 (Slider)</label>
<input type="range" id="swapSlider" min="0" max="500" value="0" class="w-full" oninput="syncSlider()">
</div>
</section>
</div>
<!-- Right Column: Visualization & Stats -->
<div class="lg:col-span-8 flex flex-col gap-6">
<!-- Chart Section -->
<section class="glass-card p-6 h-[400px] flex flex-col relative">
<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-chart-line text-emerald-400"></i> AMM Curve (x * y = k)
</h2>
<div class="text-xs bg-slate-800 px-3 py-1 rounded-full text-slate-400 border border-slate-700">
Interactive Visualization
</div>
</div>
<div class="flex-grow relative w-full h-full">
<canvas id="ammChart"></canvas>
</div>
</section>
<!-- Formula & Impact Details -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Mathematical Breakdown -->
<section class="glass-card p-6">
<h3 class="text-sm font-bold text-slate-400 uppercase mb-4 tracking-wider">수학적 공식 (The Math)</h3>
<div class="space-y-4 text-sm">
<div class="flex items-center gap-3 p-3 bg-slate-900/50 rounded-lg">
<div class="w-8 h-8 rounded-full bg-indigo-500/20 flex items-center justify-center text-indigo-400 font-serif font-bold">1</div>
<div>
<div class="text-slate-400 text-xs">불변 상수 (Constant Product)</div>
<div class="font-mono text-white"><span id="mathX">1000</span> × <span id="mathY">4000</span> = <span id="mathK">4M</span></div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-slate-900/50 rounded-lg">
<div class="w-8 h-8 rounded-full bg-pink-500/20 flex items-center justify-center text-pink-400 font-serif font-bold">2</div>
<div>
<div class="text-slate-400 text-xs">새로운 Reserve X (New X)</div>
<div class="font-mono text-white"><span id="mathOldX">1000</span> + <span id="mathDeltaX" class="text-indigo-400">0</span> = <span id="mathNewX">1000</span></div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-slate-900/50 rounded-lg">
<div class="w-8 h-8 rounded-full bg-emerald-500/20 flex items-center justify-center text-emerald-400 font-serif font-bold">3</div>
<div>
<div class="text-slate-400 text-xs">받게 될 Token B (Output)</div>
<div class="font-mono text-white">dy = Y - (k / NewX)</div>
</div>
</div>
</div>
</section>
<!-- Trade Statistics -->
<section class="glass-card p-6">
<h3 class="text-sm font-bold text-slate-400 uppercase mb-4 tracking-wider">거래 분석 (Analysis)</h3>
<div class="space-y-4">
<div class="flex justify-between items-center border-b border-slate-700 pb-3">
<span class="text-slate-400 text-sm">실행 가격 (Execution Price)</span>
<div class="text-right">
<div id="execPrice" class="font-bold text-white">0.00</div>
<div class="text-xs text-slate-500">B per A</div>
</div>
</div>
<div class="flex justify-between items-center border-b border-slate-700 pb-3">
<span class="text-slate-400 text-sm flex items-center gap-1">
가격 영향 (Price Impact)
<i class="fa-solid fa-circle-info text-[10px] text-slate-600"></i>
</span>
<div class="text-right">
<div id="priceImpact" class="font-bold text-emerald-400">0.00%</div>
</div>
</div>
<div class="flex justify-between items-center">
<span class="text-slate-400 text-sm">풀 상태 변화 (Pool Share)</span>
<div class="w-24 h-2 bg-slate-700 rounded-full overflow-hidden">
<div id="poolBar" class="h-full bg-indigo-500 transition-all duration-300" style="width: 50%"></div>
</div>
</div>
</div>
<div id="warningMsg" class="hidden mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded text-red-400 text-xs flex items-start gap-2">
<i class="fa-solid fa-triangle-exclamation mt-0.5"></i>
<span>유동성에 비해 주문 금액이 너무 큽니다. 슬리피지가 매우 높습니다.</span>
</div>
</section>
</div>
</div>
</main>
<!-- JavaScript Logic -->
<script>
// Global Variables
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'),
constK: document.getElementById('constantK'),
spotPrice: document.getElementById('spotPrice'),
mathX: document.getElementById('mathX'),
mathY: document.getElementById('mathY'),
mathK: document.getElementById('mathK'),
mathOldX: document.getElementById('mathOldX'),
mathDeltaX: document.getElementById('mathDeltaX'),
mathNewX: document.getElementById('mathNewX'),
execPrice: document.getElementById('execPrice'),
priceImpact: document.getElementById('priceImpact'),
warning: document.getElementById('warningMsg'),
poolBar: document.getElementById('poolBar')
};
// Initialization
window.onload = () => {
initChart();
updateSimulation();
// Event Listeners
els.inp.addEventListener('input', () => {
els.slider.value = els.inp.value;
updateSimulation();
});
};
function syncSlider() {
els.inp.value = els.slider.value;
updateSimulation();
}
function formatNum(num, decimals = 2) {
return parseFloat(num).toLocaleString('en-US', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
});
}
function initChart() {
const ctx = document.getElementById('ammChart').getContext('2d');
// Chart.js Configuration
chartInstance = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'Bonding Curve (k=const)',
data: [],
borderColor: '#6366f1',
borderWidth: 2,
pointRadius: 0,
fill: false,
tension: 0.4
},
{
label: 'Current State',
data: [],
backgroundColor: '#10b981',
borderColor: '#fff',
borderWidth: 2,
pointRadius: 6,
pointHoverRadius: 8
},
{
label: 'New State',
data: [],
backgroundColor: '#ec4899',
borderColor: '#fff',
borderWidth: 2,
pointRadius: 6,
pointHoverRadius: 8
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
intersect: false,
mode: 'index',
},
plugins: {
legend: {
labels: { color: '#94a3b8' }
},
tooltip: {
backgroundColor: 'rgba(15, 23, 42, 0.9)',
titleColor: '#fff',
bodyColor: '#cbd5e1',
borderColor: 'rgba(255,255,255,0.1)',
borderWidth: 1
}
},
scales: {
x: {
type: 'linear',
title: { display: true, text: 'Reserve A (🍎)', color: '#64748b' },
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: '#64748b' }
},
y: {
title: { display: true, text: 'Reserve B (🍌)', color: '#64748b' },
grid: { color: 'rgba(255, 255, 255, 0.05)' },
ticks: { color: '#64748b' }
}
}
}
});
}
function updateSimulation() {
// 1. Get Values
const x = parseFloat(els.resA.value) || 1;
const y = parseFloat(els.resB.value) || 1;
const dx = parseFloat(els.inp.value) || 0;
// 2. Calculate Constant Product
const k = x * y;
// 3. Calculate Swap
// Formula: (x + dx)(y - dy) = k
// y - dy = k / (x + dx)
// dy = y - k / (x + dx)
const newX = x + dx;
const newY = k / newX;
const dy = y - newY;
// 4. Update UI Texts
els.constK.innerText = formatNum(k, 0);
els.out.value = dx > 0 ? formatNum(dy, 4) : "0.00";
// Spot Price (Current Market Price) = y / x
const spotPrice = y / x;
els.spotPrice.innerText = `1 A = ${formatNum(spotPrice, 4)} B`;
// Execution Price = dy / dx
const execPrice = dx > 0 ? dy / dx : 0;
els.execPrice.innerText = dx > 0 ? formatNum(execPrice, 4) : "0.00";
// Price Impact = (Spot Price - Execution Price) / Spot Price * 100
let impact = 0;
if (dx > 0) {
impact = ((spotPrice - execPrice) / spotPrice) * 100;
}
// Color code impact
const impactEl = els.priceImpact;
impactEl.innerText = `${formatNum(impact, 2)}%`;
if(impact < 1) impactEl.className = "font-bold text-emerald-400";
else if(impact < 5) impactEl.className = "font-bold text-yellow-400";
else impactEl.className = "font-bold text-red-500";
// Warning
if(impact > 15) els.warning.classList.remove('hidden');
else els.warning.classList.add('hidden');
// Math Breakdown Update
els.mathX.innerText = formatNum(x, 0);
els.mathY.innerText = formatNum(y, 0);
els.mathK.innerText = formatNum(k, 0);
els.mathOldX.innerText = formatNum(x, 0);
els.mathDeltaX.innerText = formatNum(dx, 2);
els.mathNewX.innerText = formatNum(newX, 2);
// Pool Bar Visual
const totalVal = newX + newY; // Simplistic visualization
const percent = (newX / (newX + newY)) * 100; // Just showing ratio shift
// Actually, let's show price impact severity on bar
els.poolBar.style.width = `${Math.min(impact * 5, 100)}%`;
els.poolBar.className = `h-full transition-all duration-300 ${impact > 5 ? 'bg-red-500' : 'bg-indigo-500'}`;
// 5. Update Chart
updateChartData(x, y, newX, newY, k);
}
function updateChartData(x, y, newX, newY, k) {
const dataPoints = [];
// Determine range for chart (zoom out slightly based on trade)
const startX = x * 0.5;
const endX = Math.max(x * 1.5, newX * 1.2);
const steps = 50;
for (let i = 0; i <= steps; i++) {
const curX = startX + (endX - startX) * (i / steps);
const curY = k / curX;
dataPoints.push({ x: curX, y: curY });
}
chartInstance.data.datasets[0].data = dataPoints;
// Current Point (Start)
chartInstance.data.datasets[1].data = [{ x: x, y: y }];
// New Point (End) - Only if swapping
if (Math.abs(newX - x) > 0.001) {
chartInstance.data.datasets[2].data = [{ x: newX, y: newY }];
} else {
chartInstance.data.datasets[2].data = [];
}
chartInstance.update('none'); // 'none' for performance
}
</script>
</body>
</html>