idgmatrix commited on
Commit
1bbcca8
·
verified ·
1 Parent(s): f0ab587

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +639 -19
index.html CHANGED
@@ -1,19 +1,639 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Advanced DEX AMM Visualizer | AnyCoder</title>
7
+
8
+ <!-- External Libraries -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
11
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
12
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
13
+
14
+ <style>
15
+ :root {
16
+ --bg-color: #0f172a;
17
+ --card-bg: #1e293b;
18
+ --accent-primary: #6366f1;
19
+ --accent-curve: #f59e0b;
20
+ --accent-fixed: #10b981;
21
+ --text-primary: #f8fafc;
22
+ }
23
+
24
+ body {
25
+ font-family: 'Inter', sans-serif;
26
+ background-color: var(--bg-color);
27
+ color: var(--text-primary);
28
+ overflow-x: hidden;
29
+ }
30
+
31
+ /* Glassmorphism */
32
+ .glass-card {
33
+ background: rgba(30, 41, 59, 0.7);
34
+ backdrop-filter: blur(12px);
35
+ -webkit-backdrop-filter: blur(12px);
36
+ border: 1px solid rgba(255, 255, 255, 0.05);
37
+ border-radius: 16px;
38
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
39
+ transition: all 0.3s ease;
40
+ }
41
+
42
+ /* Inputs */
43
+ .custom-input {
44
+ background: rgba(15, 23, 42, 0.6);
45
+ border: 1px solid rgba(148, 163, 184, 0.2);
46
+ color: var(--text-primary);
47
+ transition: all 0.3s ease;
48
+ }
49
+ .custom-input:focus {
50
+ outline: none;
51
+ border-color: var(--accent-primary);
52
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
53
+ }
54
+
55
+ /* Tab System */
56
+ .tab-btn {
57
+ transition: all 0.3s ease;
58
+ position: relative;
59
+ overflow: hidden;
60
+ }
61
+ .tab-btn.active {
62
+ background: rgba(255, 255, 255, 0.1);
63
+ border-bottom: 2px solid;
64
+ }
65
+ .tab-btn.active[data-model="cp"] { border-color: var(--accent-primary); color: #818cf8; }
66
+ .tab-btn.active[data-model="ss"] { border-color: var(--accent-curve); color: #fbbf24; }
67
+ .tab-btn.active[data-model="cs"] { border-color: var(--accent-fixed); color: #34d399; }
68
+
69
+ /* Range Slider */
70
+ input[type=range] {
71
+ -webkit-appearance: none;
72
+ background: transparent;
73
+ }
74
+ input[type=range]::-webkit-slider-thumb {
75
+ -webkit-appearance: none;
76
+ height: 18px;
77
+ width: 18px;
78
+ border-radius: 50%;
79
+ background: #fff;
80
+ cursor: pointer;
81
+ margin-top: -7px;
82
+ box-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
83
+ }
84
+ input[type=range]::-webkit-slider-runnable-track {
85
+ width: 100%;
86
+ height: 4px;
87
+ cursor: pointer;
88
+ background: #334155;
89
+ border-radius: 2px;
90
+ }
91
+
92
+ .font-mono { font-family: 'JetBrains Mono', monospace; }
93
+
94
+ /* Math Formula Rendering */
95
+ .katex-display {
96
+ font-size: 0.9em;
97
+ overflow-x: auto;
98
+ padding: 0.5rem;
99
+ }
100
+ </style>
101
+ </head>
102
+ <body class="min-h-screen flex flex-col">
103
+
104
+ <!-- Header -->
105
+ <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">
106
+ <div class="flex items-center gap-3">
107
+ <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">
108
+ <i class="fa-solid fa-layer-group"></i>
109
+ </div>
110
+ <div>
111
+ <h1 class="text-xl font-bold tracking-tight leading-none">Multi-Model <span class="text-blue-400">AMM</span></h1>
112
+ <p class="text-xs text-slate-500 font-mono">DEX Algorithm Simulator</p>
113
+ </div>
114
+ </div>
115
+ <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">
116
+ Built with anycoder <i class="fa-solid fa-arrow-up-right-from-square text-xs"></i>
117
+ </a>
118
+ </header>
119
+
120
+ <!-- Main Content -->
121
+ <main class="flex-grow container mx-auto px-4 py-8 grid grid-cols-1 lg:grid-cols-12 gap-6">
122
+
123
+ <!-- Left Column: Controls -->
124
+ <div class="lg:col-span-4 flex flex-col gap-6">
125
+
126
+ <!-- Model Selection Tabs -->
127
+ <div class="glass-card p-2 flex justify-between gap-1">
128
+ <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">
129
+ <i class="fa-solid fa-shuffle mr-1"></i> Uniswap
130
+ </button>
131
+ <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">
132
+ <i class="fa-solid fa-bezier-curve mr-1"></i> Curve
133
+ </button>
134
+ <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">
135
+ <i class="fa-solid fa-equals mr-1"></i> Fixed
136
+ </button>
137
+ </div>
138
+
139
+ <!-- Pool Configuration -->
140
+ <section class="glass-card p-6 relative overflow-hidden">
141
+ <div class="flex justify-between items-center mb-4">
142
+ <h2 class="text-lg font-semibold flex items-center gap-2">
143
+ <i class="fa-solid fa-coins text-blue-400"></i> Liquidity Pool
144
+ </h2>
145
+ <button onclick="resetPool()" class="text-xs text-slate-500 hover:text-white underline">Reset 1:1</button>
146
+ </div>
147
+
148
+ <div class="space-y-4">
149
+ <div>
150
+ <label class="flex justify-between text-xs uppercase font-bold text-slate-500 mb-1">
151
+ <span>Reserve X (Token A)</span>
152
+ <span class="text-slate-600">Input Token</span>
153
+ </label>
154
+ <div class="flex items-center gap-2">
155
+ <span class="text-xl w-8 text-center">🍏</span>
156
+ <input type="number" id="reserveA" value="1000" class="custom-input w-full p-3 rounded-lg font-mono text-lg" oninput="updateSimulation()">
157
+ </div>
158
+ </div>
159
+
160
+ <div>
161
+ <label class="flex justify-between text-xs uppercase font-bold text-slate-500 mb-1">
162
+ <span>Reserve Y (Token B)</span>
163
+ <span class="text-slate-600">Output Token</span>
164
+ </label>
165
+ <div class="flex items-center gap-2">
166
+ <span class="text-xl w-8 text-center">🫐</span>
167
+ <input type="number" id="reserveB" value="1000" class="custom-input w-full p-3 rounded-lg font-mono text-lg" oninput="updateSimulation()">
168
+ </div>
169
+ </div>
170
+
171
+ <!-- Curve Specific Input: Amp Factor -->
172
+ <div id="ampContainer" class="hidden pt-2 border-t border-slate-700 mt-2">
173
+ <div class="flex justify-between mb-1">
174
+ <label class="text-xs uppercase font-bold text-amber-500">Amplification (A)</label>
175
+ <span id="ampValue" class="text-xs font-mono text-white">100</span>
176
+ </div>
177
+ <input type="range" id="ampSlider" min="1" max="1000" value="100" class="w-full" oninput="updateSimulation()">
178
+ <p class="text-[10px] text-slate-500 mt-1">Higher A = Flatter curve (closer to fixed price).</p>
179
+ </div>
180
+ </div>
181
+ </section>
182
+
183
+ <!-- Swap Interface -->
184
+ <section class="glass-card p-6 border-t-4 border-t-blue-500 transition-colors" id="swapCard">
185
+ <h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
186
+ <i class="fa-solid fa-right-left"></i> Swap Simulator
187
+ </h2>
188
+
189
+ <div class="relative">
190
+ <!-- Input -->
191
+ <div class="bg-slate-800/50 p-4 rounded-xl border border-slate-700 focus-within:border-blue-500/50 transition-colors">
192
+ <div class="flex justify-between mb-2">
193
+ <label class="text-xs text-slate-400">Pay (In)</label>
194
+ <span class="text-xs text-slate-500 font-mono">Token A</span>
195
+ </div>
196
+ <div class="flex items-center gap-3">
197
+ <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">
198
+ </div>
199
+ </div>
200
+
201
+ <!-- Arrow -->
202
+ <div class="absolute left-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
203
+ <div class="bg-slate-800 border border-slate-700 p-1.5 rounded-full shadow-xl text-slate-400">
204
+ <i class="fa-solid fa-arrow-down text-sm"></i>
205
+ </div>
206
+ </div>
207
+
208
+ <!-- Output -->
209
+ <div class="bg-slate-800/50 p-4 rounded-xl border border-slate-700 mt-2">
210
+ <div class="flex justify-between mb-2">
211
+ <label class="text-xs text-slate-400">Receive (Out)</label>
212
+ <span class="text-xs text-slate-500 font-mono">Token B</span>
213
+ </div>
214
+ <div class="flex items-center gap-3">
215
+ <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">
216
+ </div>
217
+ </div>
218
+ </div>
219
+
220
+ <div class="mt-4">
221
+ <input type="range" id="swapSlider" min="0" max="1000" value="0" class="w-full accent-blue-500" oninput="syncSlider()">
222
+ </div>
223
+ </section>
224
+
225
+ <!-- Stats Mini -->
226
+ <div class="glass-card p-4 flex justify-between items-center">
227
+ <div>
228
+ <div class="text-xs text-slate-500">Slippage / Impact</div>
229
+ <div id="priceImpact" class="font-bold text-emerald-400 font-mono">0.00%</div>
230
+ </div>
231
+ <div class="text-right">
232
+ <div class="text-xs text-slate-500">Execution Price</div>
233
+ <div id="execPrice" class="font-bold text-white font-mono">0.00</div>
234
+ </div>
235
+ </div>
236
+
237
+ </div>
238
+
239
+ <!-- Right Column: Visualization -->
240
+ <div class="lg:col-span-8 flex flex-col gap-6">
241
+
242
+ <!-- Chart -->
243
+ <section class="glass-card p-6 h-[450px] flex flex-col relative">
244
+ <div class="flex justify-between items-center mb-2">
245
+ <div>
246
+ <h2 class="text-lg font-semibold" id="chartTitle">Constant Product Curve</h2>
247
+ <p class="text-xs text-slate-400" id="chartSubtitle">x * y = k</p>
248
+ </div>
249
+ <div class="flex gap-2 text-xs">
250
+ <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-indigo-500"></span> Curve</span>
251
+ <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-emerald-500"></span> Current</span>
252
+ <span class="flex items-center gap-1"><span class="w-2 h-2 rounded-full bg-pink-500"></span> New</span>
253
+ </div>
254
+ </div>
255
+ <div class="flex-grow relative w-full h-full">
256
+ <canvas id="ammChart"></canvas>
257
+ </div>
258
+ </section>
259
+
260
+ <!-- Formula Explanation -->
261
+ <section class="glass-card p-6">
262
+ <h3 class="text-sm font-bold text-slate-400 uppercase mb-4 tracking-wider flex items-center gap-2">
263
+ <i class="fa-solid fa-square-root-variable"></i> Mathematical Model
264
+ </h3>
265
+
266
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
267
+ <!-- Formula Display -->
268
+ <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]">
269
+ <div id="formulaDisplay" class="font-mono text-lg text-blue-300">
270
+ x · y = k
271
+ </div>
272
+ <div id="formulaDesc" class="text-xs text-slate-500 mt-2">
273
+ Uniswap V2 Model. Ensures liquidity is never depleted.
274
+ </div>
275
+ </div>
276
+
277
+ <!-- Dynamic Variables -->
278
+ <div class="space-y-2 text-sm font-mono">
279
+ <div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
280
+ <span class="text-slate-400">Invariant (K/D)</span>
281
+ <span id="mathK" class="text-indigo-300">1,000,000</span>
282
+ </div>
283
+ <div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
284
+ <span class="text-slate-400">Current Spot Price</span>
285
+ <span id="spotPrice" class="text-emerald-400">1.0000</span>
286
+ </div>
287
+ <div class="flex justify-between p-2 bg-slate-800/30 rounded hover:bg-slate-800/50 transition">
288
+ <span class="text-slate-400">Effective Output (dy)</span>
289
+ <span id="mathDy" class="text-white">0.00</span>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </section>
294
+
295
+ </div>
296
+ </main>
297
+
298
+ <script>
299
+ // --- State Management ---
300
+ const state = {
301
+ model: 'cp', // 'cp' (Constant Product), 'ss' (StableSwap), 'cs' (Constant Sum)
302
+ resA: 1000,
303
+ resB: 1000,
304
+ amp: 100, // For Curve
305
+ input: 0
306
+ };
307
+
308
+ let chartInstance = null;
309
+
310
+ // --- DOM Elements ---
311
+ const els = {
312
+ resA: document.getElementById('reserveA'),
313
+ resB: document.getElementById('reserveB'),
314
+ inp: document.getElementById('swapInput'),
315
+ out: document.getElementById('swapOutput'),
316
+ slider: document.getElementById('swapSlider'),
317
+ ampContainer: document.getElementById('ampContainer'),
318
+ ampSlider: document.getElementById('ampSlider'),
319
+ ampValue: document.getElementById('ampValue'),
320
+ chartTitle: document.getElementById('chartTitle'),
321
+ chartSubtitle: document.getElementById('chartSubtitle'),
322
+ formulaDisplay: document.getElementById('formulaDisplay'),
323
+ formulaDesc: document.getElementById('formulaDesc'),
324
+ mathK: document.getElementById('mathK'),
325
+ spotPrice: document.getElementById('spotPrice'),
326
+ mathDy: document.getElementById('mathDy'),
327
+ impact: document.getElementById('priceImpact'),
328
+ execPrice: document.getElementById('execPrice'),
329
+ tabs: document.querySelectorAll('.tab-btn'),
330
+ swapCard: document.getElementById('swapCard')
331
+ };
332
+
333
+ // --- Initialization ---
334
+ window.onload = () => {
335
+ initChart();
336
+ updateSimulation();
337
+
338
+ els.inp.addEventListener('input', () => {
339
+ els.slider.value = Math.min(els.inp.value, 1000);
340
+ updateSimulation();
341
+ });
342
+ };
343
+
344
+ // --- Core Logic ---
345
+
346
+ function switchModel(model) {
347
+ state.model = model;
348
+
349
+ // Update Tabs UI
350
+ els.tabs.forEach(t => {
351
+ if(t.dataset.model === model) t.classList.add('active');
352
+ else t.classList.remove('active');
353
+ });
354
+
355
+ // Update Controls UI
356
+ if (model === 'ss') {
357
+ els.ampContainer.classList.remove('hidden');
358
+ els.chartTitle.innerText = "Stableswap Curve";
359
+ els.chartSubtitle.innerText = "An^n * sum(x) + D = ADn^n + D^(n+1) / (n^n * prod(x))";
360
+ els.formulaDisplay.innerHTML = "An^n \\sum x_i + D = ADn^n + \\frac{D^{n+1}}{n^n \\prod x_i}";
361
+ els.formulaDesc.innerText = "Curve Finance Model. Low slippage for like-assets (e.g., USDC/DAI).";
362
+ els.swapCard.style.borderColor = "#f59e0b";
363
+ } else if (model === 'cs') {
364
+ els.ampContainer.classList.add('hidden');
365
+ els.chartTitle.innerText = "Constant Sum";
366
+ els.chartSubtitle.innerText = "x + y = k";
367
+ els.formulaDisplay.innerText = "x + y = k";
368
+ els.formulaDesc.innerText = "Fixed Exchange Rate. Zero slippage until empty.";
369
+ els.swapCard.style.borderColor = "#10b981";
370
+ } else {
371
+ els.ampContainer.classList.add('hidden');
372
+ els.chartTitle.innerText = "Constant Product";
373
+ els.chartSubtitle.innerText = "x * y = k";
374
+ els.formulaDisplay.innerText = "x · y = k";
375
+ els.formulaDesc.innerText = "Uniswap V2 Model. Infinite liquidity range.";
376
+ els.swapCard.style.borderColor = "#6366f1";
377
+ }
378
+
379
+ updateSimulation();
380
+ }
381
+
382
+ function resetPool() {
383
+ els.resA.value = 1000;
384
+ els.resB.value = 1000;
385
+ updateSimulation();
386
+ }
387
+
388
+ function syncSlider() {
389
+ els.inp.value = els.slider.value;
390
+ updateSimulation();
391
+ }
392
+
393
+ function updateSimulation() {
394
+ // Update State
395
+ state.resA = parseFloat(els.resA.value) || 1;
396
+ state.resB = parseFloat(els.resB.value) || 1;
397
+ state.input = parseFloat(els.inp.value) || 0;
398
+ state.amp = parseInt(els.ampSlider.value);
399
+ els.ampValue.innerText = state.amp;
400
+
401
+ let output = 0;
402
+ let newY = 0;
403
+ let invariant = 0;
404
+ let spotP = 0;
405
+
406
+ // --- MATH LOGIC ---
407
+
408
+ if (state.model === 'cp') {
409
+ // x * y = k
410
+ const k = state.resA * state.resB;
411
+ invariant = k;
412
+ const newX = state.resA + state.input;
413
+ newY = k / newX;
414
+ output = state.resB - newY;
415
+ spotP = state.resB / state.resA;
416
+
417
+ } else if (state.model === 'cs') {
418
+ // x + y = k
419
+ const k = state.resA + state.resB;
420
+ invariant = k;
421
+ const newX = state.resA + state.input;
422
+ // If newX > k, pool is empty
423
+ if (newX >= k) {
424
+ output = state.resB; // Can only take what's left
425
+ newY = 0;
426
+ } else {
427
+ newY = k - newX;
428
+ output = state.resB - newY;
429
+ }
430
+ spotP = 1.0;
431
+
432
+ } else if (state.model === 'ss') {
433
+ // Stableswap Math (Simplified for 2 coins)
434
+ // D is invariant
435
+ const D = getD(state.resA, state.resB, state.amp);
436
+ invariant = D;
437
+
438
+ const newX = state.resA + state.input;
439
+ newY = getY(newX, D, state.amp);
440
+ output = state.resB - newY;
441
+
442
+ // Approx spot price (derivative at current point)
443
+ // For display, simple ratio is close enough for small dx, but let's use small delta
444
+ const smallDy = state.resB - getY(state.resA + 0.1, D, state.amp);
445
+ spotP = smallDy / 0.1;
446
+ }
447
+
448
+ // --- UI Updates ---
449
+ els.out.value = formatNum(output, 4);
450
+ els.mathDy.innerText = formatNum(output, 2);
451
+ els.mathK.innerText = formatNum(invariant, 0);
452
+ els.spotPrice.innerText = formatNum(spotP, 4);
453
+
454
+ // Execution Price & Impact
455
+ const execP = state.input > 0 ? output / state.input : 0;
456
+ els.execPrice.innerText = state.input > 0 ? formatNum(execP, 4) : "0.00";
457
+
458
+ let impact = 0;
459
+ if (state.input > 0 && spotP > 0) {
460
+ impact = ((spotP - execP) / spotP) * 100;
461
+ }
462
+ els.impact.innerText = `${formatNum(impact, 2)}%`;
463
+
464
+ // Color coding impact
465
+ if(impact < 0.1) els.impact.className = "font-bold text-emerald-400 font-mono";
466
+ else if(impact < 1.0) els.impact.className = "font-bold text-blue-400 font-mono";
467
+ else if(impact < 5.0) els.impact.className = "font-bold text-yellow-400 font-mono";
468
+ else els.impact.className = "font-bold text-red-500 font-mono";
469
+
470
+ // Update Chart
471
+ updateChart(state.resA, state.resB, state.resA + state.input, newY, invariant);
472
+ }
473
+
474
+ // --- Curve (Stableswap) Math Helpers ---
475
+ // Based on Curve Whitepaper for n=2
476
+
477
+ function getD(xp, yp, A) {
478
+ // Newton's method to find D
479
+ const S = xp + yp;
480
+ if (S === 0) return 0;
481
+
482
+ let D = S;
483
+ const Ann = A * 4; // n=2, so Ann = A * 2^2 = 4A
484
+
485
+ for (let i = 0; i < 15; i++) {
486
+ let D_P = D * D * D / (4 * xp * yp); // D^(n+1) / (n^n * prod(x)) -> D^3 / (4xy)
487
+ let prevD = D;
488
+ // Formula: D = (Ann * S + D_P * n) * D / ((Ann - 1) * D + (n + 1) * D_P)
489
+ // n=2
490
+ let num = (Ann * S + 2 * D_P) * D;
491
+ let den = (Ann - 1) * D + 3 * D_P;
492
+ D = num / den;
493
+ if (Math.abs(D - prevD) <= 1) break;
494
+ }
495
+ return D;
496
+ }
497
+
498
+ function getY(x, D, A) {
499
+ // Calculate Y given X and D
500
+ const Ann = A * 4;
501
+ const c = (D * D * D) / (4 * x * Ann); // c = D^(n+1) / (n * prod(x_others) * Ann * n^n)
502
+ const b = x + (D / Ann) - D; // b = sum(x_others) + D/Ann - D
503
+
504
+ // Solve y^2 + b*y = c
505
+ // y = ( -b + sqrt(b^2 + 4c) ) / 2 (Standard quadratic formula, since y must be positive)
506
+
507
+ // Or iterative (Curve uses iterative for gas efficiency, we can use quadratic here for JS speed for n=2)
508
+ // y^2 + (x + D/4A - D)y - D^3/(16Ax) = 0
509
+
510
+ // Let's trust the iterative method to match Curve logic exactly
511
+ let y = D;
512
+ for(let i=0; i < 15; i++){
513
+ // y = (y^2 + c) / (2y + b)
514
+ let prevY = y;
515
+ y = (y*y + c) / (2*y + b);
516
+ if(Math.abs(y - prevY) <= 1) break;
517
+ }
518
+ return y;
519
+ }
520
+
521
+
522
+ // --- Charting ---
523
+
524
+ function initChart() {
525
+ const ctx = document.getElementById('ammChart').getContext('2d');
526
+ Chart.defaults.color = '#64748b';
527
+ Chart.defaults.font.family = "'Inter', sans-serif";
528
+
529
+ chartInstance = new Chart(ctx, {
530
+ type: 'line',
531
+ data: {
532
+ datasets: [
533
+ {
534
+ label: 'Curve',
535
+ data: [],
536
+ borderColor: '#6366f1', // Will change dynamically
537
+ borderWidth: 2,
538
+ pointRadius: 0,
539
+ tension: 0.4
540
+ },
541
+ {
542
+ label: 'Current',
543
+ data: [],
544
+ backgroundColor: '#10b981',
545
+ borderColor: '#fff',
546
+ borderWidth: 2,
547
+ pointRadius: 6
548
+ },
549
+ {
550
+ label: 'New',
551
+ data: [],
552
+ backgroundColor: '#ec4899',
553
+ borderColor: '#fff',
554
+ borderWidth: 2,
555
+ pointRadius: 6
556
+ }
557
+ ]
558
+ },
559
+ options: {
560
+ responsive: true,
561
+ maintainAspectRatio: false,
562
+ plugins: {
563
+ legend: { display: false },
564
+ tooltip: {
565
+ mode: 'index',
566
+ intersect: false,
567
+ backgroundColor: 'rgba(15, 23, 42, 0.9)',
568
+ titleColor: '#fff',
569
+ bodyFont: { family: 'JetBrains Mono' }
570
+ }
571
+ },
572
+ scales: {
573
+ x: {
574
+ type: 'linear',
575
+ title: { display: true, text: 'Reserve A (Input)', color: '#94a3b8' },
576
+ grid: { color: 'rgba(255, 255, 255, 0.05)' }
577
+ },
578
+ y: {
579
+ title: { display: true, text: 'Reserve B (Output)', color: '#94a3b8' },
580
+ grid: { color: 'rgba(255, 255, 255, 0.05)' }
581
+ }
582
+ }
583
+ }
584
+ });
585
+ }
586
+
587
+ function updateChart(currX, currY, newX, newY, invariant) {
588
+ const dataPoints = [];
589
+ const steps = 100;
590
+
591
+ // Calculate range to draw
592
+ const centerX = currX;
593
+ const range = Math.max(currX, newX) * 1.5;
594
+ const startX = Math.max(1, centerX - range/2);
595
+ const endX = centerX + range/2;
596
+
597
+ // Update Line Color based on model
598
+ if(state.model === 'cp') chartInstance.data.datasets[0].borderColor = '#6366f1';
599
+ if(state.model === 'ss') chartInstance.data.datasets[0].borderColor = '#f59e0b';
600
+ if(state.model === 'cs') chartInstance.data.datasets[0].borderColor = '#10b981';
601
+
602
+ for (let i = 0; i <= steps; i++) {
603
+ const xVal = startX + (endX - startX) * (i / steps);
604
+ let yVal = 0;
605
+
606
+ if (state.model === 'cp') {
607
+ yVal = invariant / xVal;
608
+ } else if (state.model === 'cs') {
609
+ yVal = invariant - xVal;
610
+ } else if (state.model === 'ss') {
611
+ yVal = getY(xVal, invariant, state.amp);
612
+ }
613
+
614
+ if (yVal >= 0) {
615
+ dataPoints.push({ x: xVal, y: yVal });
616
+ }
617
+ }
618
+
619
+ chartInstance.data.datasets[0].data = dataPoints;
620
+ chartInstance.data.datasets[1].data = [{ x: currX, y: currY }];
621
+
622
+ if (Math.abs(newX - currX) > 0.01) {
623
+ chartInstance.data.datasets[2].data = [{ x: newX, y: newY }];
624
+ } else {
625
+ chartInstance.data.datasets[2].data = [];
626
+ }
627
+
628
+ chartInstance.update('none');
629
+ }
630
+
631
+ function formatNum(num, decimals = 2) {
632
+ return parseFloat(num).toLocaleString('en-US', {
633
+ minimumFractionDigits: decimals,
634
+ maximumFractionDigits: decimals
635
+ });
636
+ }
637
+ </script>
638
+ </body>
639
+ </html>