igneum/site/swap.html
igneum-labs 0d16878236 Home: one-line headings, the statement under the downloads, a quick-scan landing, the phone bar and the footer rows aligned
The founder's home pass (8 October 2026). Section headings shortened to one line at 390 and 1440 (What 2.0 delivers.,
The people on it.); the hero line fits one line at 1440 and keeps two at 390 (25 px; one line would need 13 px). The
one-click statement moves under the download buttons as one line. The miner rows cut to the scan shape with the claim
and window sentences verbatim and the chip detail behind /litepaper#chip-model; the deliverables table stays. The phone
bar keeps the toggle, Download and the burger together on the right at one size; the footer's icons (Discord, source,
Reddit, email) and its four download pills each sit in one row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 18:37:00 +00:00

584 lines
58 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Igneum swap on the devnet. Test tokens, no value</title>
<meta name="description" content="Swap wrapped IGN and two faucet test tokens on the Igneum 2.0 devnet, with proven execution. Every swap is a proven block; its proof status is read live. Test tokens, no value.">
<link rel="canonical" href="https://igneum.network/swap">
<meta name="theme-color" content="#0C0C0E">
<!-- share:start -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="Igneum">
<meta property="og:title" content="Igneum swap on the devnet">
<meta property="og:description" content="Swap wrapped IGN and two test tokens with proven execution. Every swap is a proven block. No value.">
<meta property="og:url" content="https://igneum.network/swap">
<meta property="og:image" content="https://igneum.network/og/swap.png?v=4">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Swap test tokens on the devnet. igneum.network/swap">
<meta property="og:image" content="https://igneum.network/og/swap-square.png?v=4">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="1200">
<meta property="og:image:alt" content="Swap test tokens on the devnet. igneum.network/swap">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Igneum swap on the devnet">
<meta name="twitter:description" content="Swap wrapped IGN and two test tokens with proven execution. Every swap is a proven block. No value.">
<meta name="twitter:image" content="https://igneum.network/og/swap.png?v=4">
<meta name="twitter:image:alt" content="Swap test tokens on the devnet. igneum.network/swap">
<!-- share:end -->
<link rel="icon" href="/favicon.ico" sizes="48x48">
<link rel="icon" href="/favicon-32.png" type="image/png" sizes="32x32">
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
<link rel="manifest" href="/site.webmanifest">
<!-- head:start -->
<link rel="preload" href="/fonts/unbounded-900.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/plex-sans-400.woff2" as="font" type="font/woff2" crossorigin>
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/site.css">
<style>
/* Shared by every page: the head partial, injected by the build between the head markers. Edit the partial, not the page. */
/* Fonts, self-hosted (latin subsets, OFL): Unbounded 500/700/900, IBM Plex Sans 400/500/600, IBM Plex Mono 400/500. Fallbacks carry size and ascent overrides so the swap does not move the layout. The tokens, type and components are in /site.css (docs/plans/site-ui-3.md). */
@font-face{font-family:'Unbounded';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/unbounded-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:700;font-display:swap;src:url(/fonts/unbounded-700.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded';font-style:normal;font-weight:900;font-display:swap;src:url(/fonts/unbounded-900.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-sans-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-sans-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url(/fonts/plex-sans-600.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/fonts/plex-mono-400.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/fonts/plex-mono-500.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Unbounded Fallback';src:local('Arial Black'),local('Arial-Black'),local('Impact');size-adjust:114%;ascent-override:87.5%;descent-override:21.5%;line-gap-override:0%}
@font-face{font-family:'Plex Sans Fallback';src:local('Arial'),local('Helvetica Neue'),local('Helvetica');size-adjust:100.5%;ascent-override:102%;descent-override:27.4%;line-gap-override:0%}
@font-face{font-family:'Plex Mono Fallback';src:local('Courier New'),local('Menlo');size-adjust:100%;ascent-override:102.5%;descent-override:27.5%;line-gap-override:0%}
</style>
<script>
/* theme before first paint: ?theme=light|dark for one view (the capture tool), else the stored choice under igneum-theme, else the system setting; ?motion=off marks the page data-motion=off so every .reveal is drawn at once (a headless render never scrolls) */
(function(){var t=null;try{t=new URLSearchParams(location.search).get("theme");}catch(e){}if(t!=="light"&&t!=="dark"){try{t=localStorage.getItem("igneum-theme");}catch(e){t=null;}}if(t!=="light"&&t!=="dark")t=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";document.documentElement.setAttribute("data-theme",t);try{if(new URLSearchParams(location.search).get("motion")==="off")document.documentElement.setAttribute("data-motion","off");}catch(e){}})();
</script>
<!-- head:end -->
<style>
/* page block; tokens, type and components are in /site.css */
main{flex:1;padding-bottom:100px}.card{background:var(--row);border:1px solid var(--line);border-radius:16px;padding:35px}.card h2{font-size:25px;margin-bottom:18px}.card h3{font-size:17px;margin:30px 0 8px}
.head{padding:65px 0 45px}.head p{color:var(--ink-2);max-width:67ch;font-size:18px;line-height:1.65}
.grid{display:grid;gap:24px;grid-template-columns:minmax(0,1fr)}
@media(min-width:900px){.grid{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:40px}}
form{display:flex;flex-direction:column;gap:12px;margin-top:16px}
label{display:block;font-size:13px;margin:16px 0 4px;color:var(--ink-2)}
input,select{background:var(--obsidian);border:1px solid var(--line-2);border-radius:7px;padding:14px 15px;color:var(--bone);width:100%;font:14px var(--f-mono)}
input:focus,select:focus{outline:2px solid var(--ember);outline-offset:2px;border-color:var(--ember)}
input[readonly]{color:var(--ash)}
.pair-row{display:grid;grid-template-columns:minmax(0,1fr) 110px;gap:10px}
.wallet-row{display:flex;flex-wrap:wrap;gap:14px;align-items:center;margin-bottom:6px}
.faucet-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px}
.btn#go{width:100%;margin-top:12px}.btn#flip{align-self:flex-start;padding:8px 14px;font-size:13px}
.btn[disabled]{opacity:.6;cursor:default;transform:none}
.status{min-height:28px;margin-top:4px;font-size:15px;color:var(--ink-2);overflow-wrap:anywhere}
.status[data-state="ok"]{color:var(--ok)}
.status[data-state="bad"]{color:var(--bad)}
.status code,td code,dd code{font-family:var(--f-mono);font-size:13px;background:var(--obsidian);padding:1px 6px;border-radius:6px}
p.small,.small{font-size:14px;color:var(--ash);line-height:1.55}
table.swaps,table.pools{width:100%;table-layout:fixed;border-collapse:collapse;font-size:14px;margin-top:8px}
table.swaps td,table.pools td,table.swaps th,table.pools th{white-space:normal;overflow-wrap:anywhere;word-break:break-word}
table.swaps th,table.pools th{text-align:left;color:var(--ash);font-weight:500;padding:8px 6px;border-bottom:1px solid var(--line)}
table.swaps td,table.pools td{padding:10px 6px;border-bottom:1px solid var(--line);vertical-align:top;white-space:normal;overflow-wrap:anywhere}
table.swaps td[data-state="bad"]{color:var(--bad)}
table.swaps tr.empty td{color:var(--ash)}
p.notice{margin-top:14px;padding:12px 14px;border-radius:10px;border:1px solid var(--line-2);font-size:13px;color:var(--molten);display:block}
dl{display:grid;grid-template-columns:auto 1fr;gap:6px 16px;margin:22px 0 0;font-size:15px}
dt{color:var(--ash)}dd{margin:0;font-family:var(--f-mono);font-size:13px;overflow-wrap:anywhere}
@media (prefers-reduced-motion:reduce){.btn:hover{transform:none}}
</style>
</head>
<body>
<!-- nav:start -->
<a class="skip" href="#main">Skip to content</a>
<nav class="nav" aria-label="Main" data-nav-version="5">
<div class="container nav-row">
<a href="/" class="logo" aria-label="Igneum home">
<svg class="brand-mark" viewBox="0 0 1024 1024" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg>
<span class="word">IGNEUM</span>
</a>
<span class="devnet" id="nav-devnet" title="igneum-devnet-4, the Igneum 2.0 devnet, chain id 4465. Coins have no value. The chain may reset."><span class="dot off" id="foot-dot"></span><span id="foot-state" data-devnet-state="live">Devnet live</span></span>
<div class="nav-groups" id="nav-groups" role="list">
<div class="nav-group" role="listitem"><button type="button" class="group-btn" id="nav-btn-mine" data-group="mine" aria-expanded="false" aria-controls="nav-panel-mine" aria-haspopup="true">Mine<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 10 5 5 5-5"/></svg></button></div>
<div class="nav-group" role="listitem"><button type="button" class="group-btn" id="nav-btn-network" data-group="network" aria-expanded="false" aria-controls="nav-panel-network" aria-haspopup="true">Network<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 10 5 5 5-5"/></svg></button></div>
<div class="nav-group" role="listitem"><button type="button" class="group-btn" id="nav-btn-learn" data-group="learn" aria-expanded="false" aria-controls="nav-panel-learn" aria-haspopup="true">Learn<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 10 5 5 5-5"/></svg></button></div>
<div class="nav-group" role="listitem"><button type="button" class="group-btn" id="nav-btn-build" data-group="build" data-active aria-expanded="false" aria-controls="nav-panel-build" aria-haspopup="true">Build<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="m7 10 5 5 5-5"/></svg></button></div>
</div>
<div class="nav-controls">
<button class="btn icon-btn" type="button" data-theme-toggle aria-label="Switch to light theme"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1 1m12 12 1 1M5 19l1-1M18 6l1-1"/></svg></button>
<a href="/download" class="btn primary small cta" data-nav="download">Download</a>
<button class="burger" id="nav-burger" type="button" aria-expanded="false" aria-controls="nav-sheet" aria-label="Open the menu" aria-haspopup="true"><svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path class="bars" d="M4 6h16M4 12h16M4 18h16"/><path class="cross" d="m6 6 12 12M6 18 18 6"/></svg></button>
</div>
</div>
<div class="panels" id="nav-panels">
<div class="panel-wrap container">
<div class="nav-panel" id="nav-panel-mine" data-group="mine" role="region" aria-labelledby="nav-btn-mine" hidden>
<div class="panel-list">
<a href="/miner" data-nav="miner"><b>Ember, the miner</b><span>One click installs the node, the miner and the prover.</span></a>
<a href="/download" data-nav="download"><b>Download</b><span>Windows, macOS, Linux and HiveOS.</span></a>
<a href="/app" data-nav="app"><b>The app</b><span>What you see while the card mines.</span></a>
<a href="/wallet" data-nav="wallet"><b>The wallet</b><span>Your coins. Final means final.</span></a>
<a href="/miners" data-nav="miners"><b>GPU bench table</b><span>Measured rates, card by card.</span></a>
<a href="/tuning" data-nav="tuning"><b>Tuning</b><span>The knee rule, the priors, the flags and the compiler settings, every figure measured.</span></a><a href="/dev-fee" data-nav="dev-fee"><b>The dev fee</b><span>The optional one, in full view, off with one flag.</span></a>
<a href="/metamask" data-nav="metamask"><b>Add to MetaMask</b><span>Igneum as a network in your wallet.</span></a>
</div>
</div>
<div class="nav-panel" id="nav-panel-network" data-group="network" role="region" aria-labelledby="nav-btn-network" hidden>
<div class="panel-list">
<a href="/live" data-nav="live"><b>Live devnet</b><span>The chain as it grows, one lane per miner.</span></a>
<a href="/explorer" data-nav="explorer"><b>Explorer</b><span>Look a block or an address up.</span></a>
<a href="/evidence" data-nav="evidence"><b>Evidence</b><span>Every claim and how far it is tested.</span></a>
<a href="/light" data-nav="light"><b>Light wallet</b><span>A balance your browser proves, trusting no node.</span></a>
<a href="/receipt" data-nav="receipt"><b>Payment receipt</b><span>A receipt any third party re-verifies offline.</span></a>
<a href="/oracle" data-nav="oracle"><b>State oracle on Sepolia</b><span>Balances on the devnet, read from another chain.</span></a>
</div>
</div>
<div class="nav-panel" id="nav-panel-learn" data-group="learn" role="region" aria-labelledby="nav-btn-learn" hidden>
<div class="panel-list">
<a href="/litepaper" data-nav="litepaper"><b>Litepaper</b><span>The design, as published.</span></a>
<a href="/income" data-nav="income"><b>Income</b><span>What your card would mine, from the live network.</span></a>
<a href="/economics" data-nav="economics"><b>Economics</b><span>The emission, the split and the fees, each with its line in the node.</span></a><a href="/scorecard" data-nav="scorecard"><b>Scorecard</b><span>The twelve acceptance gates, their evidence and where each stands.</span></a><a href="/acceptance" data-nav="acceptance"><b>Acceptance</b><span>The Test and Acceptance Standard, case by case, as each one passes.</span></a><a href="/audit" data-nav="audit"><b>Site audit</b><span>Every page, what the reset changed and what is open.</span></a><a href="/docs" data-nav="docs"><b>Documents</b><span>The plan, the standard, the specifications and the evidence files, by path.</span></a>
<a href="/ledger" data-nav="ledger"><b>Ledger</b><span>Every criticism, answered or conceded.</span></a>
<a href="/claims" data-nav="claims"><b>What Igneum does not claim</b><span>The limits, stated first.</span></a>
<a href="/randomx" data-nav="randomx"><b>Igneum vs RandomX</b><span>What was kept and what was rebuilt for GPUs.</span></a>
<a href="/provenance" data-nav="provenance"><b>Built on the shoulders</b><span>Every borrowed part, credited.</span></a>
</div>
</div>
<div class="nav-panel" id="nav-panel-build" data-group="build" role="region" aria-labelledby="nav-btn-build" hidden>
<div class="panel-list">
<a href="/build" data-nav="build"><b>Build on Igneum</b><span>Chain ids, RPC, a contract in five minutes.</span></a>
<a href="/faucet" data-nav="faucet"><b>Devnet faucet</b><span>10 IGN of devnet coin, once a day.</span></a>
<a href="/swap" data-nav="swap" aria-current="page"><b>Swap</b><span>Test tokens on the devnet; every swap is a proven block.</span></a>
<a href="/grants" data-nav="grants"><b>Grants</b><span>Tooling, apps, infrastructure, research.</span></a>
<a href="/compatibility" data-nav="compatibility"><b>Compatibility</b><span>Every row a test run against the devnet.</span></a>
</div>
</div>
</div>
</div>
<div class="nav-sheet" id="nav-sheet" role="dialog" aria-modal="true" aria-label="Menu" hidden>
<div class="sheet-scroll">
<div class="sheet-group"><div class="sheet-head">Mine</div>
<a href="/miner" data-nav="miner"><b>Ember, the miner</b><span>One click installs the node, the miner and the prover.</span></a>
<a href="/download" data-nav="download"><b>Download</b><span>Windows, macOS, Linux and HiveOS.</span></a>
<a href="/app" data-nav="app"><b>The app</b><span>What you see while the card mines.</span></a>
<a href="/wallet" data-nav="wallet"><b>The wallet</b><span>Your coins. Final means final.</span></a>
<a href="/miners" data-nav="miners"><b>GPU bench table</b><span>Measured rates, card by card.</span></a>
<a href="/tuning" data-nav="tuning"><b>Tuning</b><span>The knee rule, the priors, the flags and the compiler settings, every figure measured.</span></a><a href="/dev-fee" data-nav="dev-fee"><b>The dev fee</b><span>The optional one, in full view, off with one flag.</span></a>
<a href="/metamask" data-nav="metamask"><b>Add to MetaMask</b><span>Igneum as a network in your wallet.</span></a>
</div>
<div class="sheet-group"><div class="sheet-head">Network</div>
<a href="/live" data-nav="live"><b>Live devnet</b><span>The chain as it grows, one lane per miner.</span></a>
<a href="/explorer" data-nav="explorer"><b>Explorer</b><span>Look a block or an address up.</span></a>
<a href="/evidence" data-nav="evidence"><b>Evidence</b><span>Every claim and how far it is tested.</span></a>
<a href="/light" data-nav="light"><b>Light wallet</b><span>A balance your browser proves, trusting no node.</span></a>
<a href="/receipt" data-nav="receipt"><b>Payment receipt</b><span>A receipt any third party re-verifies offline.</span></a>
<a href="/oracle" data-nav="oracle"><b>State oracle on Sepolia</b><span>Balances on the devnet, read from another chain.</span></a>
</div>
<div class="sheet-group"><div class="sheet-head">Learn</div>
<a href="/litepaper" data-nav="litepaper"><b>Litepaper</b><span>The design, as published.</span></a>
<a href="/income" data-nav="income"><b>Income</b><span>What your card would mine, from the live network.</span></a>
<a href="/economics" data-nav="economics"><b>Economics</b><span>The emission, the split and the fees, each with its line in the node.</span></a><a href="/scorecard" data-nav="scorecard"><b>Scorecard</b><span>The twelve acceptance gates, their evidence and where each stands.</span></a><a href="/acceptance" data-nav="acceptance"><b>Acceptance</b><span>The Test and Acceptance Standard, case by case, as each one passes.</span></a><a href="/audit" data-nav="audit"><b>Site audit</b><span>Every page, what the reset changed and what is open.</span></a><a href="/docs" data-nav="docs"><b>Documents</b><span>The plan, the standard, the specifications and the evidence files, by path.</span></a>
<a href="/ledger" data-nav="ledger"><b>Ledger</b><span>Every criticism, answered or conceded.</span></a>
<a href="/claims" data-nav="claims"><b>What Igneum does not claim</b><span>The limits, stated first.</span></a>
<a href="/randomx" data-nav="randomx"><b>Igneum vs RandomX</b><span>What was kept and what was rebuilt for GPUs.</span></a>
<a href="/provenance" data-nav="provenance"><b>Built on the shoulders</b><span>Every borrowed part, credited.</span></a>
</div>
<div class="sheet-group"><div class="sheet-head">Build</div>
<a href="/build" data-nav="build"><b>Build on Igneum</b><span>Chain ids, RPC, a contract in five minutes.</span></a>
<a href="/faucet" data-nav="faucet"><b>Devnet faucet</b><span>10 IGN of devnet coin, once a day.</span></a>
<a href="/swap" data-nav="swap" aria-current="page"><b>Swap</b><span>Test tokens on the devnet; every swap is a proven block.</span></a>
<a href="/grants" data-nav="grants"><b>Grants</b><span>Tooling, apps, infrastructure, research.</span></a>
<a href="/compatibility" data-nav="compatibility"><b>Compatibility</b><span>Every row a test run against the devnet.</span></a>
</div>
<div class="sheet-foot">
<a href="/download" class="btn primary big" data-nav="download">Download</a>
<div class="sheet-social"><a href="https://discord.gg/igneum" target="_blank" rel="noopener">Discord</a><a href="https://git.igneum.network/igneum-network/spec" target="_blank" rel="noopener">Source</a><a href="https://www.reddit.com/user/Igneum_network/" target="_blank" rel="noopener">Reddit</a></div>
</div>
</div>
</div>
</nav>
<script>
(function(){
// the slim bar (site-ui-5, 7 Oct 2026): three group buttons open one panel each; the burger opens the sheet on phones.
// Keyboard: Enter or Space opens a group, Escape closes it and returns focus to its button, arrows move between the
// group buttons, Tab walks the open panel's links, a click outside closes. The theme toggle flips data-theme and stores it.
var n=document.querySelector('.nav');if(!n)return;
var btns=[].slice.call(n.querySelectorAll('.group-btn')),panels=[].slice.call(n.querySelectorAll('.nav-panel')),wrap=n.querySelector('.panels');
var burger=document.getElementById('nav-burger'),sheet=document.getElementById('nav-sheet');
function panelOf(g){return panels.filter(function(p){return p.getAttribute('data-group')===g;})[0];}
function closeAll(focusBack){var was=null;btns.forEach(function(b){if(b.getAttribute('aria-expanded')==='true')was=b;b.setAttribute('aria-expanded','false');});panels.forEach(function(p){p.hidden=true;});n.classList.remove('panel-open');if(focusBack&&was)was.focus();}
function open(g){closeAll(false);var b=btns.filter(function(x){return x.getAttribute('data-group')===g;})[0],p=panelOf(g);if(!b||!p)return;b.setAttribute('aria-expanded','true');p.hidden=false;n.classList.add('panel-open');anchor(b,p);}
// the panel sits under its button; a panel that would run past the container's right edge aligns its right edge to the button's right edge instead (never past the viewport)
function anchor(b,p){var pw=n.querySelector('.panel-wrap');if(!pw)return;var cr=pw.getBoundingClientRect(),br=b.getBoundingClientRect(),w=p.offsetWidth,left=br.left-cr.left;if(left+w>cr.width)left=(br.right-cr.left)-w;if(left<0)left=0;p.style.setProperty('--panel-left',Math.round(left)+'px');}
window.addEventListener('resize',function(){var o=btns.filter(function(x){return x.getAttribute('aria-expanded')==='true';})[0];if(o)anchor(o,panelOf(o.getAttribute('data-group')));}); btns.forEach(function(b,i){
b.addEventListener('click',function(){var on=b.getAttribute('aria-expanded')==='true';if(on)closeAll(false);else open(b.getAttribute('data-group'));});
b.addEventListener('keydown',function(e){
var k=e.key;
if(k==='ArrowRight'||k==='ArrowLeft'){e.preventDefault();var j=(i+(k==='ArrowRight'?1:btns.length-1))%btns.length;btns[j].focus();if(n.classList.contains('panel-open'))open(btns[j].getAttribute('data-group'));}
else if(k==='ArrowDown'){e.preventDefault();open(b.getAttribute('data-group'));var f=panelOf(b.getAttribute('data-group')).querySelector('a');if(f)f.focus();}
else if(k==='Escape'){closeAll(true);}
});
});
if(wrap)wrap.addEventListener('keydown',function(e){if(e.key==='Escape'){e.stopPropagation();closeAll(true);}});
document.addEventListener('click',function(e){if(!n.classList.contains('panel-open'))return;if(e.target.closest('.group-btn')||e.target.closest('.nav-panel'))return;closeAll(false);});
document.addEventListener('focusin',function(e){if(!n.classList.contains('panel-open'))return;if(n.contains(e.target))return;closeAll(false);});
document.addEventListener('keydown',function(e){if(e.key==='Escape'&&n.classList.contains('panel-open'))closeAll(true);});
// the sheet on phones
function setSheet(o){if(!burger||!sheet)return;n.classList.toggle('open',o);sheet.hidden=!o;burger.setAttribute('aria-expanded',o?'true':'false');burger.setAttribute('aria-label',o?'Close the menu':'Open the menu');document.documentElement.classList.toggle('nav-locked',o);if(o){var f=sheet.querySelector('a');if(f)f.focus();}}
if(burger&&sheet){
burger.addEventListener('click',function(){setSheet(!n.classList.contains('open'));});
sheet.addEventListener('click',function(e){if(e.target.closest('a'))setSheet(false);});
sheet.addEventListener('keydown',function(e){
if(e.key==='Escape'){setSheet(false);burger.focus();return;}
if(e.key!=='Tab')return;var f=sheet.querySelectorAll('a,button');if(!f.length)return;var first=f[0],last=f[f.length-1];
if(e.shiftKey&&document.activeElement===first){e.preventDefault();burger.focus();}else if(!e.shiftKey&&document.activeElement===last){e.preventDefault();burger.focus();}
});
burger.addEventListener('keydown',function(e){if(e.key==='Tab'&&!e.shiftKey&&n.classList.contains('open')){e.preventDefault();var f=sheet.querySelector('a');if(f)f.focus();}});
var mq=window.matchMedia('(min-width:861px)');if(mq.addEventListener)mq.addEventListener('change',function(){setSheet(false);});
}
// the theme toggle
var t=n.querySelector('[data-theme-toggle]');
function paintToggle(){var dark=document.documentElement.getAttribute('data-theme')!=='light';t.setAttribute('aria-label',dark?'Switch to light theme':'Switch to dark theme');t.innerHTML=dark?'<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1 1m12 12 1 1M5 19l1-1M18 6l1-1"/></svg>':'<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M20.7 13.3A8.5 8.5 0 0 1 10.7 3.3 8.5 8.5 0 1 0 20.7 13.3Z"/></svg>';}
if(t){t.addEventListener('click',function(){var v=document.documentElement.getAttribute('data-theme')==='light'?'dark':'light';document.documentElement.setAttribute('data-theme',v);try{localStorage.setItem('igneum-theme',v);}catch(e){}document.dispatchEvent(new CustomEvent('igneum-theme'));});paintToggle();document.addEventListener('igneum-theme',paintToggle);}
})();
</script>
<!-- nav:end -->
<main id="main" class="wrap">
<div class="head">
<div class="breadcrumb"><a href="/">Igneum</a><span>/</span><span>Swap</span></div>
<div class="eyebrow"><span class="line"></span>Devnet swap</div>
<h1>Swap test tokens <span class="accent">on the devnet.</span></h1>
<p>A constant-product exchange on Igneum, with proven execution: wrapped IGN and two faucet tokens in three pools, 0.3 percent to the pool. Every swap is a devnet block, and the block's proof status is read live below.</p>
<p class="notice">The devnet, test tokens, no value. The chain may reset and these pools go with it. Nothing here is an offer, a price or a market.</p>
</div>
<div class="grid">
<section class="card" aria-labelledby="swap-h">
<h2 id="swap-h">Swap</h2>
<div class="wallet-row">
<button class="btn" id="connect" type="button">Connect wallet</button>
<span class="status" id="wallet" role="status" aria-live="polite">No wallet connected. The Igneum Wallet open on this machine, or MetaMask, on the devnet chain id shown under Pools.</span>
</div>
<form id="swap-form" novalidate>
<label for="amount-in">You send</label>
<div class="pair-row">
<input id="amount-in" name="amount-in" inputmode="decimal" autocomplete="off" spellcheck="false" placeholder="0.0" aria-describedby="status">
<select id="token-in" aria-label="Token to send"></select>
</div>
<button class="btn ghost" id="flip" type="button" aria-label="Swap direction">Flip</button>
<label for="amount-out">You receive (quoted)</label>
<div class="pair-row">
<input id="amount-out" name="amount-out" readonly placeholder="0.0">
<select id="token-out" aria-label="Token to receive"></select>
</div>
<p class="small">The Igneum Wallet is found on this machine through 127.0.0.1; Chrome asks once per site to allow local network access, and the wallet asks once to connect igneum.network. Nothing on this page asks for a seed or a key.</p>
<p class="small" id="quote-line">Enter an amount for a quote. Slippage limit 1 percent, deadline 10 minutes.</p>
<button class="btn" id="go" type="submit" disabled>Swap</button>
<div class="status" id="status" role="status" aria-live="polite"></div>
</form>
<h3>Your swaps this session</h3>
<p class="small">Each row is a devnet block. Proof status comes from the public feed (/api/live): planned, proving, verified or paid per shard, and whether a finality checkpoint has locked the block.</p>
<table class="swaps" id="swaps" aria-label="Your swaps">
<thead><tr><th>Swap</th><th>Transaction</th><th>Block</th><th>Proof status</th></tr></thead>
<tbody id="swaps-body"><tr class="empty"><td colspan="4">No swaps yet this session.</td></tr></tbody>
</table>
</section>
<aside class="card" aria-labelledby="pools-h">
<h2 id="pools-h">Pools</h2>
<p class="small">Reserves read live from the devnet RPC. Test tokens come from each token's own faucet: one <code>drip()</code> of 1,000 an hour per address.</p>
<table class="pools" id="pools" aria-label="Pools">
<thead><tr><th>Pool</th><th>Reserves</th></tr></thead>
<tbody id="pools-body"><tr><td colspan="2">Reading the chain…</td></tr></tbody>
</table>
<p class="small" id="pools-note" role="status" aria-live="polite"></p>
<div class="faucet-row">
<button class="btn ghost" id="drip-tta" type="button" disabled>Get 1,000 TTA</button>
<button class="btn ghost" id="drip-ttb" type="button" disabled>Get 1,000 TTB</button>
</div>
<div class="status" id="faucet-status" role="status" aria-live="polite"></div>
<h3>Add liquidity</h3>
<p class="small">Deposit both sides at the pool's ratio and receive IGN-LP tokens; remove them in the same shape through the router.</p>
<form id="lp-form" novalidate>
<div class="pair-row">
<select id="lp-pool" aria-label="Pool"></select>
<input id="lp-amount" inputmode="decimal" autocomplete="off" placeholder="amount of the first token">
</div>
<p class="small" id="lp-line"></p>
<button class="btn ghost" id="lp-go" type="submit" disabled>Add liquidity</button>
<div class="status" id="lp-status" role="status" aria-live="polite"></div>
</form>
<dl>
<dt>Chain</dt><dd>igneum-devnet-4, chain id <span id="chain-id">reading…</span></dd>
<dt>RPC</dt><dd><code>https://rpc.devnet.igneum.network</code></dd>
<dt>Router</dt><dd><code class="addr" data-addr="router"></code></dd>
<dt>Factory</dt><dd><code class="addr" data-addr="factory"></code></dd>
<dt>WIGN</dt><dd><code class="addr" data-addr="wign"></code></dd>
<dt>TTA</dt><dd><code class="addr" data-addr="tta"></code></dd>
<dt>TTB</dt><dd><code class="addr" data-addr="ttb"></code></dd>
<dt>Source</dt><dd>contracts/dex in the repository; addresses in docs/contracts</dd>
<dt>Wallet set-up</dt><dd><a href="/metamask">MetaMask: chain id, RPC, one click</a> or <a href="/wallet">the Igneum Wallet</a></dd>
<dt>IGN for gas</dt><dd><a href="/faucet">The faucet</a> or <a href="/miner#get">mine a block</a></dd>
</dl>
<p class="notice small">The devnet, test tokens, no value.</p>
</aside>
</div>
</main>
<!-- footer:start -->
<footer class="site-footer">
<div class="container">
<p class="positioning">A GPU-secured network for Ethereum-compatible applications and verifiable computation.</p>
<div class="footer-grid">
<div class="footer-brand">
<a href="/" class="logo" aria-label="Igneum home"><svg class="brand-mark" viewBox="0 0 1024 1024" aria-hidden="true"><rect width="1024" height="1024" rx="160" fill="#0C0C0E"></rect><g transform="translate(166.95 166.95) scale(6.901)"><polygon points="50,4 74,34 67,58 80,54 61,96 39,96 20,54 33,58 26,34" fill="#F2541B"></polygon><polygon points="50,42 59,58 50,82 41,58" fill="#0C0C0E"></polygon></g></svg><span class="word">IGNEUM</span><span class="stop">.</span></a>
<p>Mined by GPUs. Proven by fire.</p>
<div class="social" aria-label="Igneum elsewhere">
<a href="https://discord.gg/igneum" target="_blank" rel="noopener" aria-label="The Igneum Discord"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M19.6 5.3A17 17 0 0 0 15.4 4l-.2.4a15.6 15.6 0 0 1 3.9 1.9 13.6 13.6 0 0 0-14.2 0A15.6 15.6 0 0 1 8.8 4.4L8.6 4a17 17 0 0 0-4.2 1.3C1.8 9.2 1.1 13 1.4 16.7a17.1 17.1 0 0 0 5.2 2.6l1.1-1.8a10.8 10.8 0 0 1-1.7-.8l.4-.3a12.2 12.2 0 0 0 11.2 0l.4.3-1.7.8 1.1 1.8a17 17 0 0 0 5.2-2.6c.4-4.3-.7-8-3-11.4zM8.7 14.4c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1zm6.6 0c-1 0-1.8-.9-1.8-2.1s.8-2.1 1.8-2.1 1.9 1 1.8 2.1c0 1.2-.8 2.1-1.8 2.1z"></path></svg></a>
<a href="https://git.igneum.network/igneum-network/spec" target="_blank" rel="noopener" aria-label="Igneum source: the spec, the vectors and the issues"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M8.6 17.6 3 12l5.6-5.6 1.4 1.4L5.8 12l4.2 4.2zm6.8 0-1.4-1.4L18.2 12 14 7.8l1.4-1.4L21 12z"></path></svg></a>
<a data-social="reddit" href="https://www.reddit.com/user/Igneum_network/" target="_blank" rel="noopener" aria-label="Igneum on Reddit"><svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor" aria-hidden="true"><path d="M22 12.1a2.2 2.2 0 0 0-3.7-1.6 10.8 10.8 0 0 0-5.8-1.8l1-4.6 3.2.7a1.5 1.5 0 1 0 .2-.9l-3.6-.8a.5.5 0 0 0-.5.4l-1.1 5.2a10.8 10.8 0 0 0-5.9 1.8A2.2 2.2 0 1 0 3.4 14a4.3 4.3 0 0 0 0 .7c0 3.4 3.9 6.1 8.6 6.1s8.6-2.7 8.6-6.1a4.3 4.3 0 0 0 0-.7 2.2 2.2 0 0 0 1.4-1.9zM7 13.6a1.5 1.5 0 1 1 1.5 1.5A1.5 1.5 0 0 1 7 13.6zm8.6 4.1a5.7 5.7 0 0 1-3.6 1.1 5.7 5.7 0 0 1-3.6-1.1.4.4 0 0 1 .6-.6 4.9 4.9 0 0 0 3 .9 4.9 4.9 0 0 0 3-.9.4.4 0 0 1 .6.6zm-.3-2.6a1.5 1.5 0 1 1 1.5-1.5 1.5 1.5 0 0 1-1.5 1.5z"></path></svg></a>
<a href="mailto:hello@igneum.network" aria-label="Email hello@igneum.network"><svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2"></rect><path d="m3.5 6.5 8.5 6.5 8.5-6.5"></path></svg></a>
</div>
<div class="footer-dl" aria-label="Download Ember"><a href="/download#windows"><span class="osmark mini" data-os="windows" title="Windows"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" focusable="false" fill="currentColor"><path d="M3 5.6l7.3-1v7.1H3zM11.4 4.4L21 3v8.7h-9.6zM3 12.3h7.3v7.1L3 18.4zM11.4 12.3H21V21l-9.6-1.4z"/></svg></span>Windows</a><a href="/download#mac"><span class="osmark mini" data-os="mac" title="macOS"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" focusable="false" fill="currentColor"><path d="M16.4 12.6c0-2.5 2-3.6 2.1-3.7-1.2-1.7-3-1.9-3.6-2-1.5-.2-3 .9-3.8.9-.8 0-2-.9-3.3-.8-1.7 0-3.2 1-4.1 2.5-1.8 3-.5 7.6 1.3 10.1.9 1.2 1.9 2.6 3.2 2.5 1.3 0 1.8-.8 3.3-.8 1.6 0 2 .8 3.3.8 1.4 0 2.3-1.2 3.1-2.5 1-1.4 1.4-2.8 1.4-2.9 0 0-2.7-1-2.9-4.1zM13.9 5.3c.7-.8 1.2-2 1-3.2-1 0-2.2.7-2.9 1.5-.6.7-1.2 1.9-1 3 1.1.1 2.2-.5 2.9-1.3z"/></svg></span>macOS</a><a href="/download#linux"><span class="osmark mini" data-os="linux" title="Linux"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" focusable="false" fill="currentColor"><path d="M12 2c-2.4 0-4 1.9-4 4.6 0 1.2.2 2 0 2.8-.6 1.4-2.1 2.9-2.6 4.9-.3 1.1-.1 2 .3 2.6-.6.4-1.3 1-1.1 1.7.3 1 2.1 1.2 3.2 1.8.7.4 1.5.6 2.1.1.6.2 1.3.3 2.1.3s1.5-.1 2.1-.3c.6.5 1.4.3 2.1-.1 1.1-.6 2.9-.8 3.2-1.8.2-.7-.5-1.3-1.1-1.7.4-.6.6-1.5.3-2.6-.5-2-2-3.5-2.6-4.9-.2-.8 0-1.6 0-2.8C16 3.9 14.4 2 12 2zm-1.4 4.2c.5 0 .8.5.8 1.2s-.3 1.2-.8 1.2-.8-.5-.8-1.2.3-1.2.8-1.2zm2.8 0c.5 0 .8.5.8 1.2s-.3 1.2-.8 1.2-.8-.5-.8-1.2.3-1.2.8-1.2zM12 9.3c.9 0 1.9.5 1.9 1s-1 1.2-1.9 1.2-1.9-.7-1.9-1.2 1-1 1.9-1zm0 3.4c2.2 0 3.6 2.6 3.6 4.4 0 1.5-1.6 2.3-3.6 2.3s-3.6-.8-3.6-2.3c0-1.8 1.4-4.4 3.6-4.4z"/></svg></span>Linux</a><a href="/download#hive"><span class="osmark mini" data-os="hive" title="HiveOS"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round" stroke-linecap="round"><path d="M12 2.6 20.2 7.3v9.4L12 21.4 3.8 16.7V7.3z"/><path d="M12 7.4 16 9.7v4.6L12 16.6 8 14.3V9.7z"/><path d="M12 7.4V2.6M16 9.7l4.2-2.4M16 14.3l4.2 2.4M12 16.6v4.8M8 14.3l-4.2 2.4M8 9.7 3.8 7.3"/></svg></span>HiveOS</a></div>
<a href="https://git.igneum.network/igneum-network/spec" target="_blank" rel="noopener" class="text-link">Specification and test vectors<svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 18 18 6M6 6h12v12"/></svg></a>
</div>
<div class="footer-column"><h4>Run</h4><div>
<a href="/download">Download Ember</a>
<a href="/miner">The miner</a>
<a href="/app">The app</a>
<a href="/wallet">The wallet</a>
<a href="/miners">GPU bench table</a>
<a href="/dev-fee">The dev fee</a>
<a href="/faucet">Devnet faucet</a>
<a href="/metamask">Add Igneum to MetaMask</a>
</div></div>
<div class="footer-column"><h4>Read</h4><div>
<a href="/litepaper">Litepaper</a>
<a href="/evidence">Evidence</a>
<a href="/ledger">Ledger: every criticism</a>
<a href="/claims">What Igneum does not claim</a>
<a href="/randomx">Igneum vs RandomX</a>
<a href="/provenance">Built on the shoulders</a>
</div></div>
<div class="footer-column"><h4>Explore</h4><div>
<a href="/live">Live devnet</a>
<a href="/explorer">Explorer</a>
<a href="/build">Build on Igneum</a>
<a href="/swap">Swap</a>
<a href="/grants">Grants</a>
<a href="/download">Downloads: devnet build</a>
<a href="https://discord.gg/igneum" target="_blank" rel="noopener">Community Discord</a>
<a href="mailto:hello@igneum.network">hello@igneum.network</a>
<a href="https://git.igneum.network/igneum-network/spec/issues" rel="noopener">An issue on the spec</a>
</div></div>
</div>
<div class="footer-bottom">
<p>Igneum Labs LTD · Unit IH-00-01-01-OF-01, Level 01, Innovation One, Dubai International Financial Centre. Experimental software. Nothing on this site is an offer to sell anything.<br>Report a flaw by email or by an issue on the spec. Nobody from Igneum will ask for your seed. © 2026 Igneum · not an offer to sell anything</p>
<div class="theme-group" role="group" aria-label="Theme"><button type="button" data-theme-set="system" aria-pressed="true">System</button><button type="button" data-theme-set="light" aria-pressed="false">Light</button><button type="button" data-theme-set="dark" aria-pressed="false">Dark</button></div>
</div>
</div>
</footer>
<script>
(function(){
// theme control: stores light or dark under igneum-theme; system clears it and follows the OS. The head applied the value before paint.
var bs=document.querySelectorAll('[data-theme-set]');if(!bs.length)return;
var media=window.matchMedia('(prefers-color-scheme: light)');
function stored(){try{var t=localStorage.getItem('igneum-theme');return t==='light'||t==='dark'?t:'system';}catch(e){return 'system';}}
function apply(){var c=stored();document.documentElement.setAttribute('data-theme',c==='system'?(media.matches?'light':'dark'):c);bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});document.dispatchEvent(new CustomEvent('igneum-theme'));}
bs.forEach(function(b){b.addEventListener('click',function(){var v=b.getAttribute('data-theme-set');try{if(v==='system')localStorage.removeItem('igneum-theme');else localStorage.setItem('igneum-theme',v);}catch(e){}apply();});});
if(media.addEventListener)media.addEventListener('change',function(){if(stored()==='system')apply();});
document.addEventListener('igneum-theme',function(){var c=stored();bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===c?'true':'false');});});
bs.forEach(function(b){b.setAttribute('aria-pressed',b.getAttribute('data-theme-set')===stored()?'true':'false');});
// the network state word in the status bar: one read of the observer per page view
var fs=document.getElementById('foot-state'),fd=document.getElementById('foot-dot');if(fs&&fd&&fs.getAttribute('data-devnet-state')==='live'){fetch('/api/live?window=30',{cache:'no-store'}).then(function(r){return r.json();}).then(function(d){var on=d&&d.ok&&d.state&&!d.state.stale;fs.textContent=on?'devnet live':'devnet, observer stale';fd.className='dot '+(on?'live':'off');}).catch(function(){fs.textContent='devnet';});}
})();
</script>
<!-- footer:end -->
<script src="/wallet-provider.js"></script>
<script>
(function(){
// the devnet, test tokens, no value. Addresses: docs/contracts. /wallet-provider.js (the wallet lane's page
// bridge) gives the page an EIP-1193 provider for the Igneum Wallet running on this machine: window.ethereum when no
// extension injects one, window.igneum beside MetaMask, announced through EIP-6963. Reads go to the public RPC; sends go
// through the connected wallet (EIP-1193: MetaMask and any wallet that injects window.ethereum), which asks the node
// for the gas limit itself (the devnet charges proving gas inside execution gas, so a local estimate runs out).
// the chain id is read from the RPC at load, never fixed here
var RPC='https://rpc.devnet.igneum.network', CHAIN_ID=null, CHAIN_HEX=null;
var A={router:'0x9a6fA842C4e58A87AEF1F3aD15233d99283002B7',factory:'0x09FF42dbb20448ddB673fD34F973a7D93E7139A8',wign:'0x47447783D00e1760Eb815a34707b611D53f2A51e',tta:'0x9ad4F0435f9172A89C6765eE8631CAB2Db3B5052',ttb:'0x996e3042089E80029348d7836472DB34C21841c1'};
var TOKENS=[{key:'ign',sym:'IGN',name:'IGN (the coin, wrapped on the way in)',addr:A.wign,native:true},{key:'tta',sym:'TTA',name:'Test Token A',addr:A.tta},{key:'ttb',sym:'TTB',name:'Test Token B',addr:A.ttb}];
var POOLS=[{name:'TTA / IGN',a:A.tta,b:A.wign,sa:'TTA',sb:'IGN'},{name:'TTB / IGN',a:A.ttb,b:A.wign,sa:'TTB',sb:'IGN'},{name:'TTA / TTB',a:A.tta,b:A.ttb,sa:'TTA',sb:'TTB'}];
// selectors, computed with cast sig
var S={getAmountsOut:"0xd06ca61f",swapExactTokensForTokens:"0x38ed1739",swapExactIGNForTokens:"0x5cb0bc93",swapExactTokensForIGN:"0x65462d52",approve:"0x095ea7b3",allowance:"0xdd62ed3e",balanceOf:"0x70a08231",drip:"0x9f678cca",dripWait:"0xac6f0034",getReserves:"0xd52bb6f4",addLiquidity:"0xe8e33700",addLiquidityIGN:"0x72efadf7"};
var $=function(id){return document.getElementById(id);};
var provider=null,account=null,swaps=[];
function pad(h){h=h.replace(/^0x/,'');return ('0'.repeat(64)+h).slice(-64);}
function encUint(v){return pad(BigInt(v).toString(16));}
function encAddr(a){return pad(a.toLowerCase());}
// ABI encode: types uint|address|address[]; dynamic arrays go after the head with an offset
function enc(sel,types,vals){var head=[],tail=[],headLen=types.length*32;for(var i=0;i<types.length;i++){var t=types[i],v=vals[i];if(t==='address[]'){var off=headLen+tail.join('').length/2;head.push(encUint(off));var body=encUint(v.length);for(var j=0;j<v.length;j++)body+=encAddr(v[j]);tail.push(body);}else if(t==='address')head.push(encAddr(v));else head.push(encUint(v));}return sel+head.join('')+tail.join('');}
function words(hex){hex=hex.replace(/^0x/,'');var out=[];for(var i=0;i+64<=hex.length;i+=64)out.push(BigInt('0x'+hex.slice(i,i+64)));return out;}
var rpcId=1;
function rpc(method,params){return fetch(RPC,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({jsonrpc:'2.0',id:rpcId++,method:method,params:params})}).then(function(r){return r.json();}).then(function(j){if(j.error)throw new Error(j.error.message||'rpc error');return j.result;});}
function call(to,data){return rpc('eth_call',[{to:to,data:data},'latest']);}
function fmt(wei,dp){wei=BigInt(wei);dp=dp===undefined?4:dp;var s=wei.toString().padStart(19,'0');var i=s.slice(0,-18),f=s.slice(-18,-18+dp);return i.replace(/\B(?=(\d{3})+(?!\d))/g,',')+(dp?'.'+f:'');}
function parse(str){str=String(str).trim();if(!/^\d*(\.\d{0,18})?$/.test(str)||str===''||str==='.')return null;var p=str.split('.');var f=(p[1]||'').padEnd(18,'0');return BigInt((p[0]||'0')+f);}
function short(h){return h.slice(0,10)+'…'+h.slice(-6);}
function say(el,text,state,html){el.setAttribute('data-state',state||'');if(html)el.innerHTML=html;else el.textContent=text;}
function tokenByKey(k){for(var i=0;i<TOKENS.length;i++)if(TOKENS[i].key===k)return TOKENS[i];return null;}
// ---- the wallet ----
function connect(){
var eth=window.ethereum;
if(!CHAIN_ID){say($('wallet'),'The chain id is not read yet (the public RPC did not answer). Try again in a moment.','bad');return;}
if(!eth){say($('wallet'),'No wallet found. Open the Igneum Wallet on this machine (the page finds it by itself) or install MetaMask, then tap Connect again.','bad');return;}
eth.request({method:'eth_requestAccounts'}).then(function(acc){
account=acc[0];provider=eth;
return eth.request({method:'eth_chainId'});
}).then(function(cid){
if(parseInt(cid,16)!==CHAIN_ID){
return eth.request({method:'wallet_switchEthereumChain',params:[{chainId:CHAIN_HEX}]}).catch(function(e){
if(e&&(e.code===4902||/unrecognized|not added/i.test(String(e.message))))return eth.request({method:'wallet_addEthereumChain',params:[{chainId:CHAIN_HEX,chainName:'Igneum 2.0 devnet (test tokens, no value)',nativeCurrency:{name:'IGN',symbol:'IGN',decimals:18},rpcUrls:[RPC],blockExplorerUrls:['https://igneum.network/explorer']}]});
throw e;
});
}
}).then(function(){
say($('wallet'),'',"ok",'Connected <code>'+short(account)+'</code> on chain id '+CHAIN_ID+'. The devnet, test tokens, no value.');
$('connect').textContent='Connected';$('go').disabled=false;$('lp-go').disabled=false;$('drip-tta').disabled=false;$('drip-ttb').disabled=false;
eth.on&&eth.on('accountsChanged',function(a){account=a[0]||null;if(!account){provider=null;say($('wallet'),'Wallet disconnected.','bad');}});
eth.on&&eth.on('chainChanged',function(){location.reload();});
quote();
}).catch(function(e){say($('wallet'),'The wallet refused: '+(e&&e.message?e.message:e),'bad');});
}
function send(tx){return provider.request({method:'eth_sendTransaction',params:[Object.assign({from:account},tx)]});}
function waitReceipt(hash,tries){return rpc('eth_getTransactionReceipt',[hash]).then(function(r){if(r)return r;if(tries<=0)throw new Error('no receipt yet');return new Promise(function(res){setTimeout(res,2000);}).then(function(){return waitReceipt(hash,tries-1);});});}
function ensureAllowance(token,amount){
return call(token,enc(S.allowance,['address','address'],[account,A.router])).then(function(r){
if(words(r)[0]>=amount)return null;
return send({to:token,data:enc(S.approve,['address','uint256'],[A.router,(1n<<256n)-1n])}).then(function(h){return waitReceipt(h,60);});
});
}
// ---- quotes and swaps ----
function path(tin,tout){return [tin.addr,tout.addr];}
var quoteTimer=null,lastQuote=null;
function quote(){
var tin=tokenByKey($('token-in').value),tout=tokenByKey($('token-out').value),amt=parse($('amount-in').value);
lastQuote=null;$('amount-out').value='';
if(!tin||!tout||tin.key===tout.key){say($('quote-line'),'Pick two different tokens.','');return;}
if(amt===null||amt===0n){say($('quote-line'),'Enter an amount for a quote. Slippage limit 1 percent, deadline 10 minutes.','');return;}
if(rpcBehind){say($('quote-line'),'The public RPC is still syncing; quotes come when it reaches the pools.','bad');return;}
call(A.router,enc(S.getAmountsOut,['uint256','address[]'],[amt,path(tin,tout)])).then(function(r){
var w=words(r);var out=w[w.length-1];lastQuote={tin:tin,tout:tout,amt:amt,out:out,min:out*99n/100n};
$('amount-out').value=fmt(out,6);
say($('quote-line'),'Quote: '+fmt(amt,6)+' '+tin.sym+' for '+fmt(out,6)+' '+tout.sym+' (at least '+fmt(lastQuote.min,6)+' after 1 percent slippage). 0.3 percent stays in the pool.','');
}).catch(function(e){say($('quote-line'),'No quote: '+(e.message||e),'bad');});
}
function doSwap(e){
e.preventDefault();
if(!provider||!account){say($('status'),'Connect a wallet first.','bad');return;}
if(!lastQuote){say($('status'),'Enter an amount and wait for the quote.','bad');return;}
var q=lastQuote,deadline=Math.floor(Date.now()/1000)+600,p=path(q.tin,q.tout),tx,label=fmt(q.amt,4)+' '+q.tin.sym+' to '+q.tout.sym;
$('go').disabled=true;say($('status'),'Confirm in the wallet…','');
var pre=q.tin.native?Promise.resolve():ensureAllowance(q.tin.addr,q.amt);
pre.then(function(){
if(q.tin.native)tx={to:A.router,value:'0x'+q.amt.toString(16),data:enc(S.swapExactIGNForTokens,['uint256','address[]','address','uint256'],[q.min,p,account,deadline])};
else if(q.tout.native)tx={to:A.router,data:enc(S.swapExactTokensForIGN,['uint256','uint256','address[]','address','uint256'],[q.amt,q.min,p,account,deadline])};
else tx={to:A.router,data:enc(S.swapExactTokensForTokens,['uint256','uint256','address[]','address','uint256'],[q.amt,q.min,p,account,deadline])};
return send(tx);
}).then(function(hash){
var row={label:label,hash:hash,number:null,status:'sent, waiting for a block'};swaps.unshift(row);render();
say($('status'),'',"ok",'Sent. Transaction <code>'+short(hash)+'</code>; its block appears in the table when the node has it.');
return waitReceipt(hash,90).then(function(r){row.number=parseInt(r.blockNumber,16);row.ok=r.status==='0x1';row.status=row.ok?'in block, proof status pending':'the execution failed';render();refreshProofs();refreshPools();});
}).catch(function(e){say($('status'),'Not sent: '+(e&&e.message?e.message:e),'bad');}).then(function(){$('go').disabled=false;lastQuote=null;$('amount-in').value='';$('amount-out').value='';});
}
// ---- the table and proof status from /api/live ----
function render(){
var tb=$('swaps-body');if(!swaps.length){tb.innerHTML='<tr class="empty"><td colspan="4">No swaps yet this session.</td></tr>';return;}
tb.innerHTML=swaps.map(function(s){var h=s.hash.replace(/[^0-9a-fx]/gi,'');return '<tr><td>'+s.label+'</td><td><a data-href="'+['','tx',h].join('/')+'"><code>'+short(h)+'</code></a></td><td>'+(s.number!==null?'<a data-href="'+['','block',s.number].join('/')+'">'+s.number.toLocaleString('en-GB')+'</a>':'pending')+'</td><td data-state="'+(s.ok===false?'bad':'')+'">'+s.status+'</td></tr>';}).join('');
tb.querySelectorAll('a[data-href]').forEach(function(a){a.setAttribute('href',a.getAttribute('data-href'));});
}
function proofText(b){
var sh=b.shards||[];var parts=[];
if(sh.length){var c={};sh.forEach(function(x){c[x.state]=(c[x.state]||0)+1;});var n=sh.length;if(c.paid===n)parts.push('proven and paid, '+n+' of '+n+' shards');else if((c.verified||0)+(c.paid||0)===n)parts.push('proven, '+n+' of '+n+' shards verified');else if(c.proving)parts.push('proving, '+((c.verified||0)+(c.paid||0))+' of '+n+' shards done');else parts.push('planned, '+n+' shard'+(n>1?'s':''));}
else parts.push('in the feed, proof not yet planned');
if(b.locked||b.final)parts.push('under a finality lock');
return parts.join('; ');
}
function refreshProofs(){
if(!swaps.some(function(s){return s.number!==null;}))return;
fetch('/api/live?window=1800').then(function(r){return r.json();}).then(function(j){
var byNumber={};(j.blocks||[]).forEach(function(b){if(b.number!==null&&b.number!==undefined)byNumber[b.number]=b;});
swaps.forEach(function(s){if(s.number===null||s.ok===false)return;var b=byNumber[s.number];s.status=b?proofText(b):'not yet observed on the devnet (the feed keeps the last 30 minutes)';});
render();
}).catch(function(){});
}
setInterval(refreshProofs,10000);
// ---- pools and the faucets ----
var DEPLOY_BLOCK=26972, rpcBehind=false;
function refreshPools(){
rpc('eth_blockNumber',[]).then(function(n){
var h=parseInt(n,16);rpcBehind=h<DEPLOY_BLOCK;
if(rpcBehind){$('pools-body').innerHTML='<tr><td colspan="2">RPC syncing</td></tr>';say($('pools-note'),'The public RPC is syncing: block '+h.toLocaleString('en-GB')+' of at least '+DEPLOY_BLOCK.toLocaleString('en-GB')+'. Reserves, quotes and swaps appear when it reaches the pools.','bad');return;}
say($('pools-note'),'','');
return readPools();
}).catch(function(){$('pools-body').innerHTML='<tr><td colspan="2">no answer</td></tr>';say($('pools-note'),'The public RPC did not answer. Try again in a minute.','bad');});
}
function readPools(){
Promise.all(POOLS.map(function(p){return call(A.router,enc(S.getReserves,['address','address'],[p.a,p.b])).then(function(r){var w=words(r);return fmt(w[0],2)+' '+p.sa+' / '+fmt(w[1],2)+' '+p.sb;}).catch(function(){return 'unreadable';});})).then(function(rows){
$('pools-body').innerHTML=POOLS.map(function(p,i){return '<tr><td>'+p.name+'</td><td>'+rows[i]+'</td></tr>';}).join('');
});
}
function drip(token,sym){
if(!provider||!account){say($('faucet-status'),'Connect a wallet first.','bad');return;}
call(token,enc(S.dripWait,['address'],[account])).then(function(r){var w=Number(words(r)[0]);if(w>0)throw new Error('this address can drip '+sym+' again in '+Math.ceil(w/60)+' minutes');return send({to:token,data:S.drip});})
.then(function(h){say($('faucet-status'),'',"ok",'Sent. 1,000 '+sym+' arrive with the block of <code>'+short(h)+'</code>.');return waitReceipt(h,90);}).then(function(){refreshPools();})
.catch(function(e){say($('faucet-status'),'Not sent: '+(e&&e.message?e.message:e),'bad');});
}
function lpLine(){
var p=POOLS[Number($('lp-pool').value)||0],amt=parse($('lp-amount').value);if(!p||amt===null||amt===0n){say($('lp-line'),'Enter the amount of '+ (p?p.sa:'the first token')+'; the other side follows the pool ratio.','');return;}
call(A.router,enc(S.getReserves,['address','address'],[p.a,p.b])).then(function(r){var w=words(r);var other=amt*w[1]/w[0];say($('lp-line'),fmt(amt,4)+' '+p.sa+' beside '+fmt(other,4)+' '+p.sb+' at the pool ratio.','');});
}
function addLp(e){
e.preventDefault();if(!provider||!account){say($('lp-status'),'Connect a wallet first.','bad');return;}
var p=POOLS[Number($('lp-pool').value)||0],amt=parse($('lp-amount').value);if(amt===null||amt===0n){say($('lp-status'),'Enter an amount.','bad');return;}
$('lp-go').disabled=true;say($('lp-status'),'Reading the pool…','');
call(A.router,enc(S.getReserves,['address','address'],[p.a,p.b])).then(function(r){
var w=words(r),other=amt*w[1]/w[0],deadline=Math.floor(Date.now()/1000)+600;
return ensureAllowance(p.a,amt).then(function(){
if(p.b===A.wign)return send({to:A.router,value:'0x'+other.toString(16),data:enc(S.addLiquidityIGN,['address','uint256','uint256','uint256','address','uint256'],[p.a,amt,amt*99n/100n,other*99n/100n,account,deadline])});
return ensureAllowance(p.b,other).then(function(){return send({to:A.router,data:enc(S.addLiquidity,['address','address','uint256','uint256','uint256','uint256','address','uint256'],[p.a,p.b,amt,other,amt*99n/100n,other*99n/100n,account,deadline])});});
});
}).then(function(h){say($('lp-status'),'',"ok",'Sent. Transaction <code>'+short(h)+'</code>.');return waitReceipt(h,90);}).then(function(){refreshPools();})
.catch(function(e){say($('lp-status'),'Not sent: '+(e&&e.message?e.message:e),'bad');}).then(function(){$('lp-go').disabled=false;});
}
// ---- wiring ----
function fillSelects(){
var tin=$('token-in'),tout=$('token-out');TOKENS.forEach(function(t){tin.add(new Option(t.sym,t.key));tout.add(new Option(t.sym,t.key));});tin.value='ign';tout.value='tta';
POOLS.forEach(function(p,i){$('lp-pool').add(new Option(p.name,String(i)));});
document.querySelectorAll('.addr').forEach(function(el){el.textContent=A[el.getAttribute('data-addr')];});
}
function readChainId(){return rpc('eth_chainId',[]).then(function(h){CHAIN_ID=parseInt(h,16);CHAIN_HEX='0x'+CHAIN_ID.toString(16);$('chain-id').textContent=CHAIN_ID+' ('+CHAIN_HEX+')';}).catch(function(){$('chain-id').textContent='unread (the RPC did not answer)';});}
readChainId();setInterval(readChainId,60000);
fillSelects();refreshPools();setInterval(refreshPools,30000);
// a wallet picker when both are present: the Igneum Wallet by EIP-6963, else whatever window.ethereum is
var igneumProvider=null;
window.addEventListener('eip6963:announceProvider',function(e){if(e.detail&&e.detail.info&&e.detail.info.rdns==='network.igneum.wallet')igneumProvider=e.detail.provider;});
try{window.dispatchEvent(new Event('eip6963:requestProvider'));}catch(e){}
$('connect').addEventListener('click',function(){if(igneumProvider&&window.ethereum!==igneumProvider&&window.confirm('Connect the Igneum Wallet? Cancel connects the browser extension instead.'))window.ethereum=igneumProvider;connect();});
$('swap-form').addEventListener('submit',doSwap);
$('amount-in').addEventListener('input',function(){clearTimeout(quoteTimer);quoteTimer=setTimeout(quote,300);});
$('token-in').addEventListener('change',quote);$('token-out').addEventListener('change',quote);
$('flip').addEventListener('click',function(){var a=$('token-in').value;$('token-in').value=$('token-out').value;$('token-out').value=a;quote();});
$('drip-tta').addEventListener('click',function(){drip(A.tta,'TTA');});
$('drip-ttb').addEventListener('click',function(){drip(A.ttb,'TTB');});
$('lp-pool').addEventListener('change',lpLine);$('lp-amount').addEventListener('input',lpLine);
$('lp-form').addEventListener('submit',addLp);
if(window.ethereum&&window.ethereum.selectedAddress)connect();
})();
</script>
</body>
</html>