实践 Uniswap V2 版本测试网部署

  • Oasis
  • 发布于 1天前
  • 阅读 141

UniswapV2部署与测试本文将详细说明如何使用Foundry在本地和Sepolia测试网上部署UniswapV2合约。✅已验证:本文档中的所有命令已在2025-12-01成功执行并通过测试。相关测试合约在本地及Sepolia测试网均已通过测试验证。

Uniswap V2 部署与测试

本文将详细说明如何使用 Foundry 在本地和 Sepolia 测试网上部署 Uniswap V2 合约。

已验证: 本文档中的所有命令已在 2025-12-01 成功执行并通过测试。


目录

  1. 项目概述
  2. 前置准备
  3. 项目配置
  4. 本地部署
  5. Sepolia 测试网部署
  6. 功能测试
  7. 合约验证
  8. 常见问题
  9. 部署记录

Swap 交易流程

swap_flow.png

项目结构

├── src/
│   ├── v2-core/          # Uniswap V2 核心合约 (Solidity 0.5.16)
│   ├── v2-periphery/     # Uniswap V2 外围合约 (Solidity 0.6.6)
│   └── WETH9.sol         # WETH 合约 (本地测试用)
├── script/
│   ├── DeployUniswapV2.s.sol    # 完整部署脚本
│   ├── 01_DeployFactory.s.sol   # 分步部署 - Factory
│   ├── 02_DeployRouter.s.sol    # 分步部署 - Router
│   └── 03_DeployWETH.s.sol      # 分步部署 - WETH
├── test/
│   └── UniswapV2.t.sol          # 集成测试
├── DEPLOYMENT.md                 # 详细部署文档
└── foundry.toml                  # Foundry 配置

1. 项目概述

环境说明

  • foundry 版本 v1.3.6 和 v1.5.0 (都可以成功部署)
  • win11 x64 系统
  • solidity测试和部署文件的版本为^0.8.19

核心合约

合约 路径 Solidity 版本 说明
UniswapV2Factory src/v2-core/contracts/ 0.5.16 工厂合约,用于创建交易对
UniswapV2Pair src/v2-core/contracts/ 0.5.16 交易对合约(由 Factory 自动部署)
UniswapV2Router02 src/v2-periphery/contracts/ 0.6.6 路由合约,提供用户交互接口

部署顺序

1. 部署 WETH(本地需要,Sepolia 使用已有地址)
2. 部署 UniswapV2Factory
3. 更新 init code hash(重要!)
4. 部署 UniswapV2Router02

Init Code Hash 说明

⚠️ 重要: UniswapV2Library.sol 中硬编码了 Pair 合约的 init code hash,用于计算交易对地址。如果使用 Foundry 编译,hash 值会与原版不同,必须更新

当前项目的 init code hash:

0x25aad938d8616b6e59148d3e701e4966de4418a752233589352d7c616a256568

原版 Uniswap V2 的 hash:

0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f

2. 前置准备

2.1 安装 Foundry

# Windows (使用 foundryup)
curl -L https://foundry.paradigm.xyz | bash
foundryup

# 验证安装
forge --version
cast --version
anvil --version

⚠️ 项目依赖foundry框架来部署和测试,若未安装成功foundry,请到官方仓库https://github.com/foundry-rs/foundry/releases 进行下载,windows系统下载foundry_nightly_win32_amd64包,解压后配置系统变量即可全局使用

2.2 环境变量配置

复制 .env.example.env 并填入你的值:

# 私钥(可以包含 0x 前缀)
PRIVATE_KEY=0xyour_private_key_here

# Sepolia RPC URL(可使用 Alchemy、Infura 等)
SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/your-api-key

# Etherscan API Key(用于合约验证)
ETHERSCAN_API_KEY=your_etherscan_api_key

⚠️ 安全提示

  • .env 添加到 .gitignore
  • 永远不要在公开仓库中暴露私钥
  • 测试网使用专门的测试钱包

2.3 获取 Sepolia 测试币(部署合约的gas费)


3. 项目配置

3.1 初始化项目(如果尚未初始化)

forge init --no-git  uniswap_v2  (如需git提交 后续再使用git init 命令)

Uniswap V2 核心 v2-core 和 v2-periphery 的仓库代码需要克隆到初始化后的src目录中 https://github.com/Uniswap/v2-core https://github.com/Uniswap/v2-periphery

3.2 安装依赖

forge install Uniswap/solidity-lib --no-commit

3.3 编译合约

forge build

注意:lib/solidity-lib 目录已包含必要的依赖文件,无需额外安装。


4. 本地部署

4.1 启动本地节点

# 在新终端窗口启动 Anvil
anvil

Anvil 默认配置:

  • RPC: http://127.0.0.1:8545
  • Chain ID: 31337
  • 默认私钥: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
  • 默认账户: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

4.2 部署合约(已验证命令)

# Step 1: 部署 WETH
forge create src/WETH9.sol:WETH9 \
  --rpc-url http://127.0.0.1:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  --broadcast

# Step 2: 部署 Factory(将 constructor-args 替换为你的地址)
forge create src/v2-core/contracts/UniswapV2Factory.sol:UniswapV2Factory \
  --rpc-url http://127.0.0.1:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  --broadcast \
  --constructor-args 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

# Step 3: 部署 Router(替换 FACTORY_ADDRESS 和 WETH_ADDRESS)
forge create src/v2-periphery/contracts/UniswapV2Router02.sol:UniswapV2Router02 \
  --rpc-url http://127.0.0.1:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  --broadcast \
  --constructor-args <FACTORY_ADDRESS> <WETH_ADDRESS>

4.3 验证部署

# 查询 Router 的 factory 地址
cast call <ROUTER_ADDRESS> "factory()(address)" --rpc-url http://127.0.0.1:8545

# 查询 Router 的 WETH 地址
cast call <ROUTER_ADDRESS> "WETH()(address)" --rpc-url http://127.0.0.1:8545

5. Sepolia 测试网部署

5.1 已知地址

合约 地址
WETH (Sepolia) 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9

5.2 部署命令(已验证)

以下是实际执行成功的部署命令:

# Step 1: 部署 Factory
forge create src/v2-core/contracts/UniswapV2Factory.sol:UniswapV2Factory \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY> \
  --broadcast \
  --constructor-args <YOUR_ADDRESS>

# Step 2: 部署 Router
forge create src/v2-periphery/contracts/UniswapV2Router02.sol:UniswapV2Router02 \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY> \
  --broadcast \
  --constructor-args <FACTORY_ADDRESS> 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9

5.3 验证部署

# 验证 Router 配置
cast call <ROUTER_ADDRESS> "factory()(address)" \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY>

cast call <ROUTER_ADDRESS> "WETH()(address)" \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY>

6. 功能测试(已验证)

以下是在 Sepolia 上实际执行成功的完整测试流程。

6.1 部署测试代币

# 部署 Token A (1,000,000 个)
forge create src/TestToken.sol:TestToken \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY> \
  --broadcast \
  --constructor-args "Token A" "TKA" 1000000000000000000000000

# 部署 Token B (1,000,000 个)
forge create src/TestToken.sol:TestToken \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY> \
  --broadcast \
  --constructor-args "Token B" "TKB" 1000000000000000000000000

6.2 创建交易对

# 通过 Factory 创建交易对
cast send <FACTORY_ADDRESS> \
  "createPair(address,address)" \
  <TOKEN_A_ADDRESS> <TOKEN_B_ADDRESS> \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY>

# 查询交易对地址
cast call <FACTORY_ADDRESS> \
  "getPair(address,address)(address)" \
  <TOKEN_A_ADDRESS> <TOKEN_B_ADDRESS> \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY>

6.3 授权代币

# 授权 Router 使用 Token A (授权大额度)
cast send <TOKEN_A_ADDRESS> \
  "approve(address,uint256)" \
  <ROUTER_ADDRESS> 1000000000000000000000000 \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY>

# 授权 Router 使用 Token B
cast send <TOKEN_B_ADDRESS> \
  "approve(address,uint256)" \
  <ROUTER_ADDRESS> 1000000000000000000000000 \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY>

6.4 添加流动性

# 添加 100 Token A + 100 Token B 的流动性
# deadline 使用一个足够大的时间戳 (9999999999)
cast send <ROUTER_ADDRESS> \
  "addLiquidity(address,address,uint256,uint256,uint256,uint256,address,uint256)" \
  <TOKEN_A_ADDRESS> \
  <TOKEN_B_ADDRESS> \
  100000000000000000000 \
  100000000000000000000 \
  0 \
  0 \
  <YOUR_ADDRESS> \
  9999999999 \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY>

6.5 执行 Swap

# 用 1 个 Token A 换 Token B
cast send <ROUTER_ADDRESS> \
  "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)" \
  1000000000000000000 \
  0 \
  "[<TOKEN_A_ADDRESS>,<TOKEN_B_ADDRESS>]" \
  <YOUR_ADDRESS> \
  9999999999 \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY> \
  --private-key <YOUR_PRIVATE_KEY>

6.6 查询余额

# 查询 Token B 余额
cast call <TOKEN_B_ADDRESS> \
  "balanceOf(address)(uint256)" \
  <YOUR_ADDRESS> \
  --rpc-url https://sepolia.infura.io/v3/<YOUR_API_KEY>

7. 合约验证

7.1 部署时自动验证

forge create ... \
  --verify \
  --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>

7.2 手动验证

# 验证 Factory
forge verify-contract <FACTORY_ADDRESS> \
  src/v2-core/contracts/UniswapV2Factory.sol:UniswapV2Factory \
  --chain sepolia \
  --constructor-args $(cast abi-encode "constructor(address)" <FEE_TO_SETTER>) \
  --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>

# 验证 Router
forge verify-contract <ROUTER_ADDRESS> \
  src/v2-periphery/contracts/UniswapV2Router02.sol:UniswapV2Router02 \
  --chain sepolia \
  --constructor-args $(cast abi-encode "constructor(address,address)" <FACTORY> <WETH>) \
  --etherscan-api-key <YOUR_ETHERSCAN_API_KEY>

8. 常见问题

Q1: Init Code Hash 不匹配(最常见问题)

症状: addLiquidityswap 交易失败,无明确错误信息。

原因: Router 中的 UniswapV2Library.sol 硬编码了 init code hash,与实际编译的 Pair 合约不匹配。

解决方案:

  1. 计算当前 Pair 的 init code hash:
forge inspect src/v2-core/contracts/UniswapV2Pair.sol:UniswapV2Pair bytecode > pair_bytecode.txt
  1. 在 PowerShell 中计算 hash:
$bytecode = (Get-Content pair_bytecode.txt -Raw).Trim()
if (-not $bytecode.StartsWith("0x")) { $bytecode = "0x" + $bytecode }
cast keccak256 $bytecode
  1. 更新 src/v2-periphery/contracts/libraries/UniswapV2Library.sol 第 24 行:
hex'25aad938d8616b6e59148d3e701e4966de4418a752233589352d7c616a256568' // init code hash
  1. 重新编译并部署 Router。

Q2: 交易过期 (EXPIRED)

症状: 错误信息 UniswapV2Router: EXPIRED

解决方案: 使用更大的 deadline 值,如 9999999999

Q3: Windows PowerShell 环境变量

# 方式一:直接设置
$env:PRIVATE_KEY = "0x..."
$env:SEPOLIA_RPC_URL = "https://..."

# 方式二:从 .env 文件加载
$envContent = Get-Content .env | Where-Object { $_ -match '=' -and $_ -notmatch '^#' }
foreach ($line in $envContent) {
    $parts = $line -split '=', 2
    if ($parts.Count -eq 2) {
        [Environment]::SetEnvironmentVariable($parts[0].Trim(), $parts[1].Trim(), 'Process')
    }
}

Q4: Gas 不足

forge create ... --gas-limit 500000

9. 部署记录

Sepolia 测试网部署 (2025-12-01) ✅

合约 地址 状态
Factory 0x9A267db279FE7d11138f9293784460Df577c3198 ✅ 已部署
Router 0x3c66Fe68778281be4358F21BfB63aE9cD242aB58 ✅ 已部署
WETH 0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9 (已有)
Token A (测试) 0x3aBe45b63e723eF24680afeA45B98D51bd398Bdd ✅ 已部署
Token B (测试) 0x64e6775b20Ed22ec10bca887400E6F5790112e75 ✅ 已部署
Pair (TKA/TKB) 0xF3efCad26af4AA64F2eE7a99f2051b45e9057040 ✅ 已创建

Init Code Hash: 0x25aad938d8616b6e59148d3e701e4966de4418a752233589352d7c616a256568

测试结果:

  • ✅ 创建交易对成功
  • ✅ 添加流动性成功 (100 TKA + 100 TKB)
  • ✅ Swap 成功 (1 TKA → ~0.987 TKB)

Etherscan 链接:


附录:部署检查清单

本项目源码已同步github,供参考:https://github.com/lookteas/uniswap_v2

部署前

  • [ ] Foundry 已安装 (forge --version)
  • [ ] .env 配置正确
  • [ ] 钱包有足够的 ETH
  • [ ] UniswapV2Library.sol 的 init code hash 已更新

部署后

  • [x] Factory 已部署
  • [x] Router 已部署
  • [x] Router.factory() 返回正确的 Factory 地址
  • [x] Router.WETH() 返回正确的 WETH 地址
  • [x] 创建交易对成功
  • [x] 添加流动性成功
  • [x] Swap 功能正常
点赞 0
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Oasis
Oasis
0x1213...Bc79
江湖只有他的大名,没有他的介绍。