从零构建去中心化交易所(DEX)DEMO:完整技术实现指南

发布时间:2026/8/16 23:25:20

从零构建去中心化交易所(DEX)DEMO:完整技术实现指南
引言构建一个去中心化交易所DEX是从零理解DeFi世界的最佳实践路径。本文将完整记录从合约设计、核心模块开发到前端集成的全过程基于Uniswap V2架构实现一个功能完整的DEX DEMO涵盖AMM交易模型、流动性池、Swap交易逻辑、Router/Factory/Pair核心合约、钱包连接及链上交互等全部核心模块。一、技术选型与项目初始化1.1 技术栈层级技术选型智能合约Solidity ^0.8.20 OpenZeppelin合约开发框架Foundry推荐或 Hardhat前端框架React 18 TypeScript链上交互ethers.js v6构建工具ViteUI组件Ant DesignFoundry是当前Solidity开发的最佳实践工具链以Rust编写测试速度极快。若选择Hardhat则更适合JavaScript/TypeScript生态的开发者-。1.2 项目结构dex-demo/ ├── contracts/ │ ├── core/ │ │ ├── DEXFactory.sol # 工厂合约 │ │ ├── DEXPair.sol # 交易对合约 │ │ └── DEXERC20.sol # LP代币 │ ├── periphery/ │ │ ├── DEXRouter.sol # 路由合约 │ │ └── WETH.sol # 封装ETH │ └── test/ │ └── TestToken.sol # 测试代币 ├── frontend/ │ ├── src/ │ │ ├── hooks/ # 自定义Hooks │ │ ├── components/ # UI组件 │ │ └── utils/ # 工具函数 └── test/ └── DEX.t.sol # Foundry测试二、智能合约层Core-Periphery架构DEX的核心采用Core-Periphery模式—— Core合约提供基础安全和核心逻辑Periphery合约提供用户友好的交互接口-。2.1 核心合约Pair交易对Pair合约是AMM的核心实现了恒定乘积做市商模型。恒定乘积公式Pair合约核心实现// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import openzeppelin/contracts/token/ERC20/IERC20.sol; import openzeppelin/contracts/security/ReentrancyGuard.sol; contract DEXPair is ReentrancyGuard { address public factory; address public token0; address public token1; uint112 private reserve0; uint112 private reserve1; uint32 private blockTimestampLast; uint public constant MINIMUM_LIQUIDITY 10**3; mapping(address uint) public balanceOf; uint public totalSupply; constructor() { factory msg.sender; } // 初始化池子仅首次添加流动性时调用 function initialize(address _token0, address _token1) external { require(msg.sender factory, DEX: FORBIDDEN); token0 _token0; token1 _token1; } // 添加流动性 - mint LP代币 function mint(address to) external nonReentrant returns (uint liquidity) { (uint112 _reserve0, uint112 _reserve1, ) getReserves(); uint balance0 IERC20(token0).balanceOf(address(this)); uint balance1 IERC20(token1).balanceOf(address(this)); uint amount0 balance0 - _reserve0; uint amount1 balance1 - _reserve1; uint _totalSupply totalSupply; if (_totalSupply 0) { liquidity Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY; _mint(address(0), MINIMUM_LIQUIDITY); // 永久锁定最小流动性 } else { liquidity Math.min( amount0 * _totalSupply / _reserve0, amount1 * _totalSupply / _reserve1 ); } require(liquidity 0, DEX: INSUFFICIENT_LIQUIDITY_MINTED); _mint(to, liquidity); _update(balance0, balance1, _reserve0, _reserve1); } // 移除流动性 - burn LP代币 function burn(address to) external nonReentrant returns (uint amount0, uint amount1) { (uint112 _reserve0, uint112 _reserve1, ) getReserves(); uint balance0 IERC20(token0).balanceOf(address(this)); uint balance1 IERC20(token1).balanceOf(address(this)); uint liquidity balanceOf[address(this)]; uint _totalSupply totalSupply; amount0 liquidity * balance0 / _totalSupply; amount1 liquidity * balance1 / _totalSupply; require(amount0 0 amount1 0, DEX: INSUFFICIENT_LIQUIDITY_BURNED); _burn(address(this), liquidity); _safeTransfer(token0, to, amount0); _safeTransfer(token1, to, amount1); _update(balance0 - amount0, balance1 - amount1, _reserve0, _reserve1); } // 核心Swap函数 function swap(uint amount0Out, uint amount1Out, address to) external nonReentrant { require(amount0Out 0 || amount1Out 0, DEX: INSUFFICIENT_OUTPUT_AMOUNT); (uint112 _reserve0, uint112 _reserve1, ) getReserves(); require(amount0Out _reserve0 amount1Out _reserve1, DEX: INSUFFICIENT_LIQUIDITY); uint balance0 IERC20(token0).balanceOf(address(this)); uint balance1 IERC20(token1).balanceOf(address(this)); if (amount0Out 0) _safeTransfer(token0, to, amount0Out); if (amount1Out 0) _safeTransfer(token1, to, amount1Out); uint balance0Adjusted balance0 - amount0Out; uint balance1Adjusted balance1 - amount1Out; // 恒定乘积检查包含0.3%手续费 require( balance0Adjusted * balance1Adjusted uint(_reserve0) * uint(_reserve1) * 1000**2, DEX: K ); _update(balance0, balance1, _reserve0, _reserve1); } // 更新储备 function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private { reserve0 uint112(balance0); reserve1 uint112(balance1); blockTimestampLast uint32(block.timestamp % 2**32); } }Pair合约的关键设计要点使用nonReentrant防止重入攻击-1采用CEIChecks-Effects-Interactions模式-1最小流动性锁定机制防止价格操控手续费0.3%通过调整k值隐式收取2.2 工厂合约FactoryFactory合约负责创建和管理所有交易对使用CREATE2实现确定性地址部署。contract DEXFactory { mapping(address mapping(address address)) public getPair; address[] public allPairs; event PairCreated(address indexed token0, address indexed token1, address pair, uint); function createPair(address tokenA, address tokenB) external returns (address pair) { require(tokenA ! tokenB, DEX: IDENTICAL_ADDRESSES); (address token0, address token1) tokenA tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(token0 ! address(0), DEX: ZERO_ADDRESS); require(getPair[token0][token1] address(0), DEX: PAIR_EXISTS); bytes memory bytecode type(DEXPair).creationCode; bytes32 salt keccak256(abi.encodePacked(token0, token1)); assembly { pair : create2(0, add(bytecode, 32), mload(bytecode), salt) } DEXPair(pair).initialize(token0, token1); getPair[token0][token1] pair; getPair[token1][token0] pair; allPairs.push(pair); emit PairCreated(token0, token1, pair, allPairs.length); } }Factory负责的核心功能-创建Pair合约记录所有交易对通过getPair查询池子地址-2.3 外围合约RouterRouter是用户交互的入口提供了更友好的接口封装-contract DEXRouter { address public immutable factory; address public immutable WETH; constructor(address _factory, address _WETH) { factory _factory; WETH _WETH; } // 添加流动性ETH版本 function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity) { // 实现略 } // 精确输入兑换 function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts) { // 实现略 } // 获取兑换金额 function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts) { // 实现略 } }Router支持的核心功能-精确输入/输出兑换滑点保护amountOutMin参数截止时间控制deadline参数多跳路由path[]数组-三、AMM交易模型详解3.1 价格计算与滑点.2 手续费机制DEX的手续费设计采用0.3%交易费100%归LP的模式交易者支付0.3%的手续费手续费留在池子中增加reservesLP通过持有LP代币按比例分享手续费收益-协议费用0.05%可通过feeTo地址收取--6手续费通过在swap中调整k值实现对amountIn扣手续费后计算实际进池数量-1。3.3 流动性提供LP机制LP通过添加流动性获得LP代币作为份额证明-四、前端开发与链上交互4.1 钱包连接ethers.js v6前端使用ethers.js v6的BrowserProvider连接MetaMask-import { BrowserProvider, ethers } from ethers; // 连接钱包 const connectWallet async () { if (!window.ethereum) { throw new Error(请安装MetaMask); } const provider new BrowserProvider(window.ethereum); await provider.send(eth_requestAccounts, []); const signer await provider.getSigner(); const address await signer.getAddress(); return { provider, signer, address }; };Web3前端与合约交互的本质流程-用ethers.js把用户操作编码成calldata让钱包签名通过RPC广播链上执行后读回events/storage刷新UI4.2 Swap交互实现// 执行Swap const executeSwap async ( router: ethers.Contract, path: string[], amountIn: bigint, amountOutMin: bigint, to: string, deadline: number ) { const tx await router.swapExactTokensForTokens( amountIn, amountOutMin, path, to, deadline ); const receipt await tx.wait(); return receipt; }; // 估算输出金额 const getAmountsOut async ( router: ethers.Contract, amountIn: bigint, path: string[] ): Promisebigint[] { return await router.getAmountsOut(amountIn, path); };前端关键注意点-27必须在发起swap之前先估算amountOut-27前端计算minAmountOut amountOut * (1 - slippage)并提交链上-27显示price impact、pool depth和expected fee-27正确处理ERC20的decimals-27使用estimateGas预估Gas防止交易失败-274.3 滑点保护实现滑点保护是DEX前端最重要的安全机制const swapWithSlippageProtection async ( router: ethers.Contract, amountIn: bigint, path: string[], slippageTolerance: number // 例如 0.005 0.5% ) { const amounts await router.getAmountsOut(amountIn, path); const amountOut amounts[amounts.length - 1]; const minAmountOut amountOut * BigInt(Math.floor((1 - slippageTolerance) * 1000)) / 1000n; // 执行swap传入minAmountOut作为滑点保护 return await router.swapExactTokensForTokens( amountIn, minAmountOut, path, await signer.getAddress(), Math.floor(Date.now() / 1000) 60 * 20 // 20分钟截止 ); };五、测试与部署5.1 Foundry测试Foundry是当前Solidity测试的最佳工具// test/DEX.t.sol contract DEXTest is Test { DEXFactory factory; DEXPair pair; TestToken tokenA; TestToken tokenB; function setUp() public { tokenA new TestToken(Token A, TKNA); tokenB new TestToken(Token B, TKNB); factory new DEXFactory(); address pairAddr factory.createPair(address(tokenA), address(tokenB)); pair DEXPair(pairAddr); } function testSwap() public { // 添加流动性 tokenA.approve(address(pair), 1000 ether); tokenB.approve(address(pair), 1000 ether); pair.mint(address(this)); // 执行Swap uint reserve0Before pair.reserve0(); uint reserve1Before pair.reserve1(); pair.swap(100 ether, 0, address(this)); // 验证... } }完整的测试应覆盖-27正常流程测试添加流动性、兑换、移除流动性异常场景测试滑点保护、截止时间Fuzz测试Foundry fuzz静态检查Slither模糊测试Echidna5.2 部署流程完整的部署流程-6部署Factory合约记录工厂合约地址和init code hash-6部署WETH合约记录WETH地址-6修改Router中的hash不要带0x前缀-6部署Router合约传入WETH和工厂合约地址-6部署前端配置合约地址和ABI5.3 安全审计要点DEX安全设计的核心关注点-27风险类型防护措施重入攻击CEI模式 nonReentrant修饰符-1闪电贷/价格操控使用TWAP或外部预言机-27滑点攻击前端强制滑点设置 显示price impact-27整数溢出Solidity ^0.8自带检查-27初始流动性攻击锁定最小流动性MINIMUM_LIQUIDITY-27ERC20兼容性使用SafeERC20-1六、Demo运行指南6.1 环境准备# 安装Foundry curl -L https://foundry.paradigm.xyz | bash foundryup # 克隆项目 git clone your-repo cd dex-demo # 安装依赖 forge install # 编译合约 forge build6.2 运行测试# 运行所有测试forge test# 查看Gas报告forge test --gas-report# 运行特定测试forge test --match-contract DEXTest --match-test testSwap6.3 启动前端cd frontend npm install npm run dev前端功能清单Swap - 代币兑换基于AMM算法Liquidity - 流动性添加/移除Pool - 流动性池管理钱包连接MetaMask滑点保护和价格影响提示七、总结与展望本文从零构建了一个完整的DEX DEMO涵盖Core-Periphery合约架构Factory管理Pair创建Pair实现AMM核心逻辑Router提供用户友好接口-AMM交易模型恒定乘积公式×x×yk驱动价格发现-11流动性池机制LP通过添加流动性获得LP代币并分享0.3%手续费-前端集成ethers.js v6实现钱包连接和链上交互-安全设计CEI模式、滑点保护、重入防护-1-27这个DEMO虽然是简化版但涵盖了DEX的核心技术要素可作为深入理解DeFi的起点。生产环境还需考虑TWAP预言机、多跳路由、协议费用治理、更完善的测试覆盖等进阶特性-27。未来的DEX将向更高性能Layer2扩容、更强可编程性Hooks机制和更深流动性聚合方向演进。而理解这个从0到1的构建过程正是掌握这些前沿技术的最佳起点。

相关新闻

Windows批处理脚本:从零到精通的自动化利器

Windows批处理脚本:从零到精通的自动化利器

2026/8/16 23:25:20

1. 从“黑框框”到效率神器:Windows批处理脚本的现代解读 如果你用过Windows电脑,大概率见过那个黑底白字的“命令提示符”窗口。很多人觉得它神秘又有点吓人,是“高手”或者“搞破坏”的人才用的东西。但你可能不知道,这个看似古…

为LLM智能体注入情商:PsychoAgent情感感知与冲突管理架构实践

为LLM智能体注入情商:PsychoAgent情感感知与冲突管理架构实践

2026/8/16 23:15:20

1. 项目缘起:当LLM智能体开始“闹情绪” 最近在折腾LLM智能体(Agent)时,我遇到了一个挺有意思的瓶颈。我们团队当时在做一个多智能体协作的模拟项目,比如让几个智能体模拟一个产品团队的日常会议。理想很丰满&#xff…

还在手动维护股票代码列表?你的量化选股脚本可能已经漏了几百只票

还在手动维护股票代码列表?你的量化选股脚本可能已经漏了几百只票

2026/8/16 23:15:20

做量化选股、全市场扫描或定时监控脚本时,许多开发者第一反应都是在本地存一个 stocks.txt、stocks.json,或者直接在 Python 代码里硬编码写一个列表: # 常见的硬编码股票列表 symbols ["600519.SH", "000001.SZ", &qu…

浏览器资源嗅探终极指南:猫抓插件三步轻松搞定网页视频下载

浏览器资源嗅探终极指南:猫抓插件三步轻松搞定网页视频下载

2026/8/17 1:45:29

浏览器资源嗅探终极指南:猫抓插件三步轻松搞定网页视频下载 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 上周帮朋友保存一门在线课程…

多智能体LLM校准:反事实图技术提升协作可靠性与效率

多智能体LLM校准:反事实图技术提升协作可靠性与效率

2026/8/17 1:45:29

1. 项目概述:当多智能体大模型需要“校准”时,我们在谈论什么?最近在折腾多智能体(Multi-Agent)系统,特别是基于大语言模型(LLM)构建的智能体协作网络时,一个绕不开的痛点…

浏览器资源嗅探终极指南:猫抓插件,把网页视频和图片一键装进口袋

浏览器资源嗅探终极指南:猫抓插件,把网页视频和图片一键装进口袋

2026/8/17 1:45:29

浏览器资源嗅探终极指南:猫抓插件,把网页视频和图片一键装进口袋 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 刷到一门特…

Windows平台Git安装配置全攻略

Windows平台Git安装配置全攻略

2026/8/17 1:45:29

1. Git 安装配置完全指南作为一名长期使用Git进行版本控制的开发者,我深知一个正确的安装配置过程对后续开发效率的影响。很多新手在初次接触Git时,往往因为配置不当导致各种奇怪的问题。本文将基于Windows平台,带你从零开始完成Git的完整安装…

从零搭建OSRM开源路线规划引擎:自主可控的高性能路径计算服务

从零搭建OSRM开源路线规划引擎:自主可控的高性能路径计算服务

2026/8/17 1:45:29

1. 从零开始:为什么你需要一个自己的OSRM服务?如果你处理过任何与地理位置相关的数据,比如规划配送路线、分析用户出行轨迹,或者开发一个需要“从A到B怎么走最快”功能的应用,那你大概率听说过或使用过像Google Maps D…

Alist挂载阿里云盘报错invalid X-Device-Id的排查与修复指南

Alist挂载阿里云盘报错invalid X-Device-Id的排查与修复指南

2026/8/17 1:35:29

1. 问题现象与背景:当你的Alist挂载阿里云盘突然“罢工”如果你正在使用Alist来统一管理你的阿里云盘文件,并且已经稳定运行了一段时间,那么某天打开Alist后台或者尝试访问文件时,突然看到类似failed get objs: failed to list ob…

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

2026/8/17 1:28:42

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

【双层规划,节点出清价,绿证交易,CVaR方法】两级电力市场环境下计及风险的省间交易商最优购电模型附Matlab代码

2026/8/16 0:04:13

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

隐式mpc+自适应mpc+时变mpc,线性时变模型预测控制附Simulink仿真

2026/8/16 0:04:13

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

2026/8/17 0:05:22

1. 项目概述:为什么异步调用是LabVIEW进阶的必修课? 如果你用LabVIEW做过稍微复杂点的项目,尤其是涉及界面响应、多任务并行或者硬件IO等待的场景,大概率遇到过这样的窘境:前面板点个按钮,整个程序就“卡死…

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

LabVIEW异步调用实战:解决界面卡顿与并行处理难题

2026/8/17 0:05:22

1. 项目概述:为什么异步调用是LabVIEW进阶的必经之路如果你在LabVIEW里写过稍微复杂点的程序,尤其是涉及到界面响应、多任务并行或者硬件IO等待,大概率会遇到一个头疼的问题:程序“卡”住了。前面板点不动,进度条不更新…

飞书局域网文件传输实战:3种方案实现高速点对点传输

飞书局域网文件传输实战:3种方案实现高速点对点传输

2026/8/17 0:05:22

1. 项目概述:为什么要在局域网内用飞书传文件? 飞书作为一款主流的协同办公套件,其核心功能是围绕云端协作设计的。无论是文档、表格还是文件,通常的分享逻辑都是“上传到云端 -> 生成链接 -> 分享给同事”。这个流程在互联…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/15 1:04:46

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/15 10:10:27

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/14 19:35:14

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…