惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 三生石上(FineUI控件)
T
Threat Research - Cisco Blogs
月光博客
月光博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
爱范儿
爱范儿
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
云风的 BLOG
云风的 BLOG
D
Docker
罗磊的独立博客
U
Unit 42
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
P
Proofpoint News Feed
博客园 - Franky
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
B
Blog RSS Feed
美团技术团队
J
Java Code Geeks
S
Securelist
Cyberwarzone
Cyberwarzone
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
NISL@THU
NISL@THU
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Recorded Future
Recorded Future
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
LINUX DO - 热门话题
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
A
About on SuperTechFans
MongoDB | Blog
MongoDB | Blog
Spread Privacy
Spread Privacy
T
Tenable Blog
I
Intezer
N
News | PayPal Newsroom
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX - 技术
S
Schneier on Security
S
SegmentFault 最新的问题
Latest news
Latest news
宝玉的分享
宝玉的分享
V
Visual Studio Blog
V
V2EX
T
Tor Project blog
C
Comments on: Blog

DEV Community

How to Build a Power BI Financial Dashboard for Healthcare Where Tensor-Parallel Inference Hits the NVLink Wall I open-sourced a World Cup 2026 prediction model — and tested it honestly Database WAL Bloat Management: The Core Anatomy for Performance WordPress Emails Were Failing Silently on DigitalOcean. Here's What Broke. Reading Belgium's KBO/CBE registry: what the live API returns 🤫 I Built CodeMoji: A VS Code Extension That Turns Code Into Emojis 5 AI Pair Programming Patterns That Actually Speed Up Development LLD Object-Oriented Design: From Requirements to Classes (Bridging Thinking to Domain Modeling) How We Built a CTO-Grade Grafana Dashboard With Codex How We Built a CTO-Grade Grafana Dashboard With Codex T-Slot Bolts and Nuts for Secure Industrial Clamping From Tools to Economic Actors: Why AI Agents Need Independent Financial Infrastructure Designing a Scalable Event-Driven Data Processing Pipeline with Apache Kafka Streams M4 Pro vs M5 Pro: Which Apple Silicon Chip Wins? Calling GET API & Mapping Response in VBCS (Service Connection) How to Connect a Trailhead Org to Salesforce CLI WebAssembly - Why Your Browser Can Finally Do What Desktop Apps Could I built a CLI tool to find worthwhile GitHub issues to contribute to I Built a Global Location Data API with 12M+ Cities — Here's How Why we built a metadata-driven Angular framework instead of using Retool Your PR Queue Is the New Technical Debt. AI Code Review Is the Fix Nobody Set Up Yet. Hermes Quant: Zero-Cost Autonomous Equity Research Agent Powered by Hermes 3 RedBase / redb.Route / redb.Tsak 3.0.0 shipped Road To KiwiEngine #5: The Future of SaaS Might Be Operational Ownership Comparison pages that say where the competition wins Devlog #3 Turning OpenClaw Governance Into an Operating Layer AI Governance as Infrastructure on AGTP How Instagram, WhatsApp, Uber & Netflix Would Be Built Today Using Expo Router Linux: A few more tips From Zero to Manifest V3: How GitHub Copilot Helped Me Finish an RSC Vulnerability Detector for CVE‑2025‑55182 Best use of Gemini in everyday life. How to configure Claude Code (and Cursor) so it stops ignoring your conventions The AI Agent Ecosystem in PHP - From Simple OpenAI Calls to Multi-Agent Platforms Building a Micro-SaaS Empire: A Step-by-Step Guide to Creating and Monetizing Open-Source Developer Tools Google's Agentic Leap: How Gemini Turned Workspace Into Your Autonomous Executive Assistant The Art of Package Publishing: Best Practices for Creating and Maintaining Popular Open-Source Libraries Architectural Foundations of Adobe Experience Manager: A Developer's Deep Dive (Part - 1) Automating Bulk Content Authoring in SitecoreAI with PowerShell Extensions I tried to hide semantic meaning from embeddings without breaking search Static vs Non-Static in Java: Understanding Class and Object Through a Shop Story Selling a macOS app outside the App Store is easy. Licensing is the hard part. The Agent That Actually Remembers You: A Deep Dive into Hermes Agent published What Is HTTP & HTTPS ? Frontend Architecture: Where Does This File Go? W. Edwards Deming: The Father of Total Quality Who Predicted the Future of AI Vue Teleport component React: How does VuReact convert it? How to audit an AI agent skill: the 7-check framework we used on 200 skills AI for Knowledge Management: Real Workflows That Hold Up W. Edwards Deming: El Padre de la Calidad Total que Predijo el Futuro de la IA Mamba/SSM Basics 4 Ways to Get Started with AITuber, Sorted by Level How I Fixed a CORS Error Without Knowing Backend - and What I Learned From It Using jQuery to hide a DIV when the user clicks outside of it. How to Build a PostgreSQL Backed Job Queue in Go Manifest AI联创Jacob谈Transformer的不足与提出 Power Retention Gemma Mentor AI: From an Unfinished Prototype to a Real-Time Multi-Agent Learning Companion Giving Your Digital Employee a Company Credit Card (With Limits) Your AI Assistant Just Bought a $30,000 Cloud Subscription How Writing Can Help You Escape AI Delirium Self-Hosted VPN in 2026: WireGuard, Headscale, NetBird and More Compared Build Cache Strategies: The Operational Burden of Speed Inside agent-gov: Architecture of an Agent Cost Governance Platform Why we chose a Rust template engine and Go APIs Announcing agent-gov: Open-Source AI Agent Cost Governance AI doesn't fail because the model is bad. It fails because there's nothing underneath it Polly wants a transcript: giving agents ears and a voice, on your own machine AI coding assistants make junior devs faster and worse at the same time AI Won't Save You From Forgetting How to Think encodeURI vs encodeURIComponent: The JavaScript URL Encoding Trap Building an MCP Server Using Spring AI, JSON-RPC and SSE (Server-Sent Events) PgBouncer: Effectively Managing Your PostgreSQL Connection Pool How much should I charge for 3D prints? A complete pricing breakdown for Etsy sellers How to Fix Core Web Vitals in a MERN Stack App (Complete Guide) Is AI-Native .NET Development Actually Happening in 2026? The 54-point production deployment checklist that saves you from 3am rollbacks How I Built Hidden Collector Game in Unity Moving Beyond the Context Window: The Agentic Memory Architecture 🚀 Building an open-source email blast tool — free, self-hosted, no Mailchimp needed. Looking for contributors to help add: 📊 Open & click tracking 🐳 Docker support All issues are open. Jump in 👇 https://github.com/nikhilt101/email-blast-tool Progressive Distillation System Design - 6.CAP Theorem & PACELC, CAP Theorem & PACELC: The Most Important Trade-off in Distributed Systems [Boost] AWS Summit India Online 2026: Get a FREE AWS Certificate Without Any Exam or Fees! 🚀 Why your React tournament bracket breaks in Safari (and a 4 KB pure-CSS fix) Markdown Is Becoming the AI App Interface AstroFit – My Fitness Tracking Web Application When WP-CLI fatals on the plugin you came to rescue # Agentic AI: Architecture of Autonomous Systems Why Most AI Agents Forget Everything — And Why Hermes Agent Changes the Game Hermes Agent's Brain: How Its Skills & Memory System Actually Works How to Structure Reusable Components in a Next.js Project Scribe vs ClickTrek vs Tango vs Guidde vs Floik: Workflow Documentation Tools Compared (2026) Awk! Awk! Add a diagram!: Greptile-style PR diagrams, minus the SaaS How to Share Client Links Safely: Custom URLs, Passwords, and Expiration Dates The AI Agent That Deleted Everything in 9 Seconds — And What Every Developer Needs to Know I built a time-decaying knowledge graph for my terminal — here's how it works Demystifying Linux File Permissions and chmod (Without the Guesswork) I Let Claude Design 4 Chaos Experiments via MCP. The 4th Took Down Staging and Found a 6-Month-Old Bug. Two agents passing strings to each other is not a multi-agent system — it's a pipeline, and the distinction matters System Design - 5.Latency vs Throughput Latency vs Throughput: Why "Average Response Time" Is the Biggest Lie in Engineering
A .NET Dinosaur in Web3. Day 18 - Automated Market Maker
Olena · 2026-05-31 · via DEV Community

🏦 Day 6 of 7: Building a Mini Uniswap in 80 Lines of Solidity

Imagine a vending machine. It has 1,000 coffee beans and 1,000 coins. No menu, no cashier — just one iron rule: the product of the two numbers inside must never decrease.

That's it!

This is how Uniswap works — and this is what I built on Day 6, coming from .NET. Here's how, why it's elegant, and where you can step on a rake.

Why an Order Book Doesn't Work on a Blockchain

Traditional exchanges — Binance, NYSE, any CEX — run on an order book. Market makers post bids and asks. A matching engine pairs them. Millions of updates per second, all in a centralised database.

In a blockchain, this is impossible. Transactions take 12 seconds. Every state change costs gas. Storing millions of constantly changing orders would eat all the profit before a single trade completes.

Uniswap's solution: replace the order book with a liquidity pool — a smart contract holding two tokens — and replace the matching engine with pure math.

Just a formula — below.

x · y = k — The Formula That Broke Finance

The Constant Product Invariant:

x · y = k

Enter fullscreen mode Exit fullscreen mode

Where x is the reserve of Token0, y is the reserve of Token1, and k is a constant that must never decrease during swaps.

When a trader sells Token0 into the pool, x increases. To keep k constant, y must decrease — the contract sends out Token1. The price is determined automatically by the ratio of reserves.

Live example with numbers:

Pool: 1,000 Token0, 1,000 Token1. k = 1,000,000.

Trader sells 100 Token0:

amountOut = (reserveOut × amountIn) / (reserveIn + amountIn)
amountOut = (1000 × 100) / (1000 + 100)
amountOut = 100,000 / 1,100
amountOut ≈ 90.9 Token1

Enter fullscreen mode Exit fullscreen mode

The trader gets ~90.9, not 100. That gap is slippage — and it's not a bug. It's the formula protecting the pool. The more you buy relative to pool size, the worse your price gets. Naturally. Mathematically.

After the swap: pool has 1,100 Token0 and ~909.1 Token1. k ≈ 1,000,000. Invariant holds.

The Contract: SimpleAMM

Three functions. Each one exists for a specific reason.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SimpleAMM {
    error ZeroAmount();
    error InvalidToken();
    error ZeroLiquidity();
    error TransferFailed();
    error InvalidRatio();

    IERC20 public immutable token0;
    IERC20 public immutable token1;

    // Internal reserves — cheaper than calling balanceOf() every time
    uint256 public reserve0;
    uint256 public reserve1;

    event LiquidityAdded(address indexed provider, uint256 amount0, uint256 amount1);
    event Swap(address indexed trader, address tokenIn, uint256 amountIn, uint256 amountOut);

    constructor(address _token0, address _token1) {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
    }

    // Pure math — no state, no side effects
    function getAmountOut(uint256 _amountIn, uint256 _reserveIn, uint256 _reserveOut)
        public pure returns (uint256)
    {
        if (_amountIn == 0) revert ZeroAmount();
        if (_reserveIn == 0 || _reserveOut == 0) revert ZeroLiquidity();

        // Multiply first, divide last — always
        uint256 numerator = _reserveOut * _amountIn;
        uint256 denominator = _reserveIn + _amountIn;
        return numerator / denominator;
    }

    function addLiquidity(uint256 _amount0, uint256 _amount1) external {
        if (_amount0 == 0 || _amount1 == 0) revert ZeroAmount();

        // If pool already has liquidity, enforce the current price ratio
        if (reserve0 > 0 && reserve1 > 0) {
            if (_amount0 * reserve1 != _amount1 * reserve0) revert InvalidRatio();
        }

        if (!token0.transferFrom(msg.sender, address(this), _amount0)) revert TransferFailed();
        if (!token1.transferFrom(msg.sender, address(this), _amount1)) revert TransferFailed();

        reserve0 += _amount0;
        reserve1 += _amount1;

        emit LiquidityAdded(msg.sender, _amount0, _amount1);
    }

    function swap(address _tokenIn, uint256 _amountIn) external returns (uint256 amountOut) {
        if (_amountIn == 0) revert ZeroAmount();
        if (_tokenIn != address(token0) && _tokenIn != address(token1)) revert InvalidToken();

        bool isToken0 = _tokenIn == address(token0);

        (IERC20 tokenIn, IERC20 tokenOut, uint256 reserveIn, uint256 reserveOut) = isToken0
            ? (token0, token1, reserve0, reserve1)
            : (token1, token0, reserve1, reserve0);

        // CEI: pull tokens in first
        if (!tokenIn.transferFrom(msg.sender, address(this), _amountIn)) revert TransferFailed();

        // Calculate output
        amountOut = getAmountOut(_amountIn, reserveIn, reserveOut);

        // Update reserves
        if (isToken0) {
            reserve0 += _amountIn;
            reserve1 -= amountOut;
        } else {
            reserve0 -= amountOut;
            reserve1 += _amountIn;
        }

        emit Swap(msg.sender, _tokenIn, _amountIn, amountOut);

        // Send output tokens to trader
        if (!tokenOut.transfer(msg.sender, amountOut)) revert TransferFailed();
    }
}

Enter fullscreen mode Exit fullscreen mode

getAmountOut — pure math, no state. Separated deliberately so it can be called by anyone to preview a trade before executing it. In DeFi this is standard: quote first, then transact.

addLiquidity — the ratio check is the interesting part. If the pool already has reserves, you can't deposit in arbitrary proportions. _amount0 * reserve1 != _amount1 * reserve0 detects any imbalance. Deposit skewed amounts and you'd instantly change the price — essentially donating money to arbitrageurs.

swap — the ternary tuple assignment is the cleanest part of the contract. Instead of two separate if/else branches, one line maps all four variables correctly based on direction:

(IERC20 tokenIn, IERC20 tokenOut, uint256 reserveIn, uint256 reserveOut) = isToken0
    ? (token0, token1, reserve0, reserve1)
    : (token1, token0, reserve1, reserve0);

Enter fullscreen mode Exit fullscreen mode

Where You Can Step on a Rake

Integer division truncates, silently.

getAmountOut divides at the end — intentionally. But the truncation still happens. 100,000 / 1,100 = 90, not 90.909.... The pool keeps the remainder. At scale across millions of trades, this accumulated dust is non-trivial. Production AMMs handle this with basis points (fee = 30 bps = multiply by 997/1000 before dividing).

Internal reserves vs balanceOf.

The contract tracks reserve0 and reserve1 internally instead of calling token0.balanceOf(address(this)) every time. Two reasons: gas savings (SLOAD is expensive, external calls are more expensive), and security — if someone sends tokens directly to the contract without going through addLiquidity, the reserves won't silently become unbalanced and break the invariant.

Console Verification Flow

npx hardhat ignition deploy ignition/modules/SimpleAMM.ts --network localhost --reset
npx hardhat console --network localhost

Enter fullscreen mode Exit fullscreen mode

const { viem } = await network.create();
const [owner, trader] = await viem.getWalletClients();
const cViem = require("viem");

const t0Address = "0x5FbDB2315678afecb367f032d93F642f64180aa3";
const t1Address = "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512";
const ammAddress = "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0";

const token0 = await viem.getContractAt("MyToken", t0Address);
const token1 = await viem.getContractAt("MyToken", t1Address);
const amm = await viem.getContractAt("SimpleAMM", ammAddress);

// Trader buys tokens via ICO
const t0AsTrader = await viem.getContractAt("MyToken", t0Address, { client: { wallet: trader } });
const t1AsTrader = await viem.getContractAt("MyToken", t1Address, { client: { wallet: trader } });
await t0AsTrader.write.buyTokens({ value: cViem.parseEther("2") }); // 2000 Token0
await t1AsTrader.write.buyTokens({ value: cViem.parseEther("2") }); // 2000 Token1

// Add liquidity 1000:1000
const ammAsTrader = await viem.getContractAt("SimpleAMM", ammAddress, { client: { wallet: trader } });
await t0AsTrader.write.approve([ammAddress, cViem.parseEther("1000")]);
await t1AsTrader.write.approve([ammAddress, cViem.parseEther("1000")]);
await ammAsTrader.write.addLiquidity([cViem.parseEther("1000"), cViem.parseEther("1000")]);

console.log("Reserve 0:", cViem.formatEther(await amm.read.reserve0())); // 1000
console.log("Reserve 1:", cViem.formatEther(await amm.read.reserve1())); // 1000

// Swap 100 Token0 → Token1
await t0AsTrader.write.approve([ammAddress, cViem.parseEther("100")]);
await ammAsTrader.write.swap([t0Address, cViem.parseEther("100")]);

const traderT1Balance = await token1.read.balanceOf([trader.account.address]);
console.log("Trader Token1 after swap:", cViem.formatEther(traderT1Balance));
// ~1090.909... — math checks out

Enter fullscreen mode Exit fullscreen mode

The formula lands exactly. 1,000 × 100 / 1,100 = 90.909... Token1 received. The invariant holds.

What This Day Actually Meant

Six days ago I was writing owner = msg.sender in a constructor. Today I implemented the core pricing engine of a decentralised exchange.

What transferred directly from .NET:

  • CEI pattern — same as any transactional system
  • Separation of pure logic (getAmountOut) from state mutation (swap) — same as keeping domain logic out of controllers
  • Defensive checks before any state change — same as guard clauses

What was genuinely new:

  • Thinking in invariants instead of conditions
  • Price as an emergent property of reserves, not a stored value
  • The elegance of x · y = k — one line that replaces an entire matching engine

What's Next

Day 7: Reentrancy Protection — the vulnerability that cost $60M in the 2016 DAO hack, and how to write contracts that can't be drained.


Repo: github.com/alena-dev-soft

Follow the journey on Telegram: t.me/dotnetToWeb3

Stage: Dinosaur 🦕 — going deeper into the bedrock. Day 6 of 7.