从零构建去中心化交易所(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批处理脚本:从零到精通的自动化利器

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

2026/8/16 23:25:48 阅读更多 →
Axivion 扩展:让代码质量与架构守护融入开发流程

Axivion 扩展:让代码质量与架构守护融入开发流程

Qt Creator 利器:Axivion 扩展完全指南——让代码质量与架构守护融入开发流程 做 Qt 开发的同学大多有过类似体会:项目越迭代越大,代码越写越庞杂,慢慢就会出现重复代码、莫名的循环依赖、没人敢动的 “僵尸代码”、偏离编码规范的写法…… 这些问题不会立刻让程序崩溃,但…

2026/8/16 23:25:48 阅读更多 →
使用Docker Compose部署BookStack:构建私有知识库的完整实践指南

使用Docker Compose部署BookStack:构建私有知识库的完整实践指南

1. 项目概述与核心价值 最近在整理个人项目和团队文档时,一直在寻找一个既美观又实用的知识库系统。要求很简单:能像写书一样结构化地组织内容,支持Markdown,权限管理要清晰,最关键的是部署和维护要足够简单。在试用了…

2026/8/16 23:25:48 阅读更多 →

最新新闻

nslookup命令使用说明

nslookup命令使用说明

个人建站,域名备案完成后,往往还要做域名解析服务,技术人员怎么能知道自己配置的DNS正确与否呢?NSLOOKUP查询域名信息的一个非常有用的命令,可以指定查询的类型,可以查到DNS记录的生存时间还可以指定使用哪…

2026/8/17 0:00:08 阅读更多 →
【原创唯一】基于SpringBoot+Vue的在线书店商城系统 课程设计/大作业/期末作业(源码+MySQL数据库+实验报告+PPT+远程部署)

【原创唯一】基于SpringBoot+Vue的在线书店商城系统 课程设计/大作业/期末作业(源码+MySQL数据库+实验报告+PPT+远程部署)

摘要 电子商务与移动支付的普及,线上购书已成为高校师生及社会公众获取图书的重要方式。传统线下书店在图书检索、库存查询、订单跟踪等方面存在信息分散、效率较低等问题。本文设计并实现了一套基于 B/S 架构的网上书店系统,采用前后端分离模式&#xf…

2026/8/17 0:00:08 阅读更多 →
飞书局域网文件传输实战:3种方案实现高速点对点传输

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

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

2026/8/17 0:00:08 阅读更多 →
LabVIEW异步调用实战:解决界面卡顿与并行处理难题

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

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

2026/8/17 0:00:08 阅读更多 →
LabVIEW异步调用实战:从原理到生产者消费者模式,解决界面卡顿与并行处理难题

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

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

2026/8/17 0:00:08 阅读更多 →
错误分享:误将磁盘分区类型选成磁盘名称

错误分享:误将磁盘分区类型选成磁盘名称

1.先删除原有分区2.fdisk重新创建3.发现进程被占用4.使用kill关不掉进程,加 -9 强制关闭5.关闭后重新使用fdisk创建,tips:记得改完后要使用 w 保存

2026/8/16 23:59:08 阅读更多 →

日新闻

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

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

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

2026/8/17 0:00:08 阅读更多 →
LabVIEW异步调用实战:解决界面卡顿与并行处理难题

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

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

2026/8/17 0:00:08 阅读更多 →
飞书局域网文件传输实战:3种方案实现高速点对点传输

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

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

2026/8/17 0:00:08 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/16 0:00:54 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/16 0:00:55 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

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

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

2026/8/16 0:03:55 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/16 6:00:23 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/16 6:00:24 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/16 6:00:27 阅读更多 →