想在主网之外安全玩透 Uniswap V3?这篇指南把从环境到代码、再到单元测试的完整流程拆解透彻,开发者只需一步步跟着做,就能快速拥有可复用的 Uniswap 开发环境,并亲手完成第一个链上代币兑换合约。
目录
- 环境准备:Hardhat + 主网分叉
- 克隆官方样板项目
- 编写 SimpleSwap 合约
- 单元测试:使用分叉主网验证逻辑
- 常见问题 FAQ
- 进阶路线图
环境准备:一键启动本地 Hardhat + 主网分叉
1. 安装工具链
Uniswap 官方并没有限定框架,但日常开发最顺手的组合是:
- Hardhat:本地节点、部署脚本、测试框架三合一
- Alchemy:免费额度足够 fork 主网,省去自己跑 archive 节点的成本
2. 创建 Alchemy 账号并获取 API Key
- 打开 https://www.alchemy.com/ 注册账号(免费)。
- 点击「Create App」→ 选择 Ethereum → Mainnet → 生成 API Key。
把这个 Key 保存好,下一步就要用到。
3. 启动主网分叉节点
# 把 {YOUR_API_KEY} 换成上一步生成的字符串
npx hardhat node --fork https://eth-mainnet.alchemyapi.io/v2/{YOUR_API_KEY}
出现 Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
你就成功了:
此刻本地 8545 端口跑的是一条完全复制主网状态的测试链,所有 Uniswap Pool、Pair、Factory 地址俱全,gas 还是假的,随便玩。
克隆官方样板项目
不想从零搭脚手架?直接切到官方预先配好的仓库即可:
git clone https://github.com/Uniswap/uniswap-first-contract-example.git
cd uniswap-first-contract-example
npm install # 安装依赖
仓库已内置
@uniswap/v3-periphery
相关接口hardhat.config.js
配置了主网 fork 字段- 空合约示例与测试骨架
你可以把精力集中在业务逻辑,而非编译配置。
编写 SimpleSwap 合约
步骤 1:初始化文件
打开 contracts/SimpleSwap.sol
,骨架已给出。先把关键常量补全:
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol';
import '@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol';
contract SimpleSwap {
ISwapRouter public immutable swapRouter;
address public constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint24 public constant FEE_TIER = 3000; // 0.3%
constructor(ISwapRouter _swapRouter) {
swapRouter = _swapRouter;
}
}
步骤 2:实现 swapWETHForDAI
新增对外调用函数,实现精确输入模式(exact input):
function swapWETHForDAI(uint256 amountIn) external returns (uint256 amountOut) {
// 1. 把用户的 WETH 拉到合约
TransferHelper.safeTransferFrom(
WETH9, msg.sender, address(this), amountIn
);
// 2. 授权 SwapRouter 花这笔 WETH
TransferHelper.safeApprove(
WETH9, address(swapRouter), amountIn
);
// 3. 组装参数并执行兑换
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: WETH9,
tokenOut: DAI,
fee: FEE_TIER,
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
amountOut = swapRouter.exactInputSingle(params);
return amountOut;
}
至此,合约逻辑写完:拉起金额 → 授权 → 交易,一行多余代码都没有。
单元测试:在“分叉主网”跑通交易
Test 文件位置
test/SimpleSwap.test.js
已搭好骨架,只需填充。
Test 流程步骤
- 部署
SimpleSwap
,注入主网 SwapRouter 地址。 - 取 Hardhat 自带账户,把 10 ETH wrap 出 10 WETH。
- 授权合约可划 1 WETH。
- 调用
swapWETHForDAI(0.1 WETH)
。 - 断言 DAI 余额增加。
关键代码片段:
const DAIBalanceBefore = Number(
ethers.utils.formatUnits(await DAI.balanceOf(signer.address), 18)
);
await WETH.connect(signer).approve(simpleSwap.address, ethers.utils.parseEther('1'));
await simpleSwap.connect(signer).swapWETHForDAI(ethers.utils.parseEther('0.1'));
const DAIBalanceAfter = Number(
ethers.utils.formatUnits(await DAI.balanceOf(signer.address), 18)
);
expect(DAIBalanceAfter).to.be.greaterThan(DAIBalanceBefore);
运行测试
确保此前用 npx hardhat node --fork ...
启动的节点仍在后台,然后:
npx hardhat test --network localhost
看到绿色 ✔ Should provide a caller with more DAI than they started with after a swap
代表流程一步到位,主网 fork 环境已可反复利用。
常见问题 FAQ
Q1:如果我在本地测试没问题,如何部署到测试网?
A:在 hardhat.config.js
里新增 goerli
网络配置,把 INFURA_KEY 或 Alchemy Goerli Key 放进去;然后在 Goerli 领取测试 ETH + 新版 WETH 与 DAI(可直接用 Uniswap 的测试币“水龙头楼”),最后 npx hardhat run scripts/deploy.js --network goerli
即可。
Q2:mainnet fork 遇到块高延迟怎么办?
A:在 node 启动命令加 --fork-block-number 17100000
固定块高,既能提速又能避免同名 Token 迁移带来的干扰。
Q3:amountOutMinimum
设 0 会不会被“三明治”攻击?
A:会。示例仅用于学习。实际生产建议:链下把期望最小输出量计算好再传进来,或用 Quoter
合约实时采样。
Q4:可以把 swapWETHForDAI
做成任意 ERC-20 之间的互换吗?
A:当然可以。改造方向:通过 ExactInputParams
构造多跳路径;再用 Path
库拼接 token → pool → token 字符串即可。
进阶路线图
做完最基础的 WETH→DAI 一对一交换后,可以挑战以下“作业”,每一个都能深度学习 Uniswap 深层设计:
- 实现精确输出交换函数(拉 DAI 限量,反推所需 WETH)
- 写一个通用 Swap 合约,支持任意 ERC-20 Pair
- 做一个链上报价合约,链下可读链上不可执行交易,避免浪费 gas
- 架设前端,参考测试脚本里的 ethers.js 逻辑,用户只需 MetaMask 即可在网页一键 Swap
若你准备把“纸上谈兵”变成活生生的产品,👉 立即查看如何安全部署到主网还能监控实时价格。
结语
一次部署、多次复用:Hardhat + 主网分叉 + Uniswap V3 正成为链上开发者最丝滑的“三件套”。
把这份 SimpleSwap 作为起点,再加上你的创意,无论做 DEX 聚合器、收益策略还是游戏内兑换,都将拥有世界第一深度流动性做后盾。你的下一个 DeFi 爆款,也许就从这一行 exactInputSingle
开始。