如何使用Uniswap创建自有LP代币搭建LP挖矿测试应用
Uniswap V2 自有LP代币+简易测试流动性挖矿实现参考
核心概念澄清
你不用手动提前发行LP代币:Uniswap V2体系下的LP是交易对持仓凭证,由UniswapV2Factory合约在创建新交易对时自动部署对应的LP合约,第一次注入初始流动性时才会铸造实际的LP份额,全程不需要你手动编写LP的ERC20逻辑。
注意不要搞混角色:LP是用户参与流动性挖矿的质押凭证,你要发的挖矿奖励是你自己的项目代币,别把LP直接当奖励代币发。
分步实现代码示例
以下示例可以直接在Hardhat/Foundry测试环境、Sepolia测试网跑,不需要修改Uniswap核心合约源码。
1. 依赖准备
- 提前准备两个ERC20代币:一个是你自己的项目代币(作为挖矿奖励),另一个是和它组交易对的基础资产(本地测试可以自己部署mock WETH/USDC,测试网可以用公开的测试币合约)
- 直接用对应网络的Uniswap V2官方部署合约地址即可,本地测试如果要从头部署,直接用Uniswap公开的核心合约源码部署Factory、Router就行,不需要改逻辑。
2. 创建交易对,生成LP合约地址
调用Factory的createPair方法即可自动部署对应交易对的LP合约,返回值就是LP代币的合约地址,本身已经实现了标准ERC20的所有方法:
// 精简版Factory接口,不需要引入全量合约代码 interface IUniswapV2Factory { function createPair(address tokenA, address tokenB) external returns (address pair); function getPair(address tokenA, address tokenB) external view returns (address pair); } // 对应网络的Uniswap V2 Factory、Router地址,本地/测试网替换成对应部署地址即可 address constant UNISWAP_V2_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; address constant UNISWAP_V2_ROUTER = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; // 创建你自己的代币交易对,拿到LP地址 function deployMyPair(address myProjectToken, address baseToken) external returns (address lpTokenAddr) { lpTokenAddr = IUniswapV2Factory(UNISWAP_V2_FACTORY).createPair(myProjectToken, baseToken); }
3. 添加初始流动性,铸造第一份LP
拿到LP地址后,调用Router的addLiquidity方法注入初始流动性,方法会自动给你铸造对应数量的LP代币:
interface IUniswapV2Router02 { function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); } // 加流动性前记得给Router授权两个代币的转账额度 function addInitLiquidity(address myProjectToken, address baseToken, uint amountMyToken, uint amountBase) external { IERC20(myProjectToken).approve(UNISWAP_V2_ROUTER, amountMyToken); IERC20(baseToken).approve(UNISWAP_V2_ROUTER, amountBase); // 返回的liquidity就是你实际拿到的LP数量,会直接转入你的钱包地址 IUniswapV2Router02(UNISWAP_V2_ROUTER).addLiquidity( myProjectToken, baseToken, amountMyToken, amountBase, 0, // 测试环境滑点容忍设为0即可,生产环境需要根据实际调整 0, msg.sender, block.timestamp + 600 ); }
4. 极简LP挖矿合约核心逻辑
拿到LP地址后,就可以写挖矿合约,核心逻辑是接收用户质押的LP,按持仓占比给用户发奖励代币,以下是可直接用于测试的精简版逻辑:
// 精简版LP挖矿合约 contract SimpleLPMiner { IERC20 public immutable stakingLp; // 前面步骤生成的LP地址 IERC20 public immutable rewardCoin; // 你自己的项目奖励代币 uint public rewardPerBlock; // 每个区块发放的奖励数量 mapping(address => uint) public userStaked; uint public totalStakedLp; uint public lastUpdateBlock; uint public accRewardPerLp; constructor(address _lpAddr, address _rewardAddr, uint _rewardPerBlock) { stakingLp = IERC20(_lpAddr); rewardCoin = IERC20(_rewardAddr); rewardPerBlock = _rewardPerBlock; lastUpdateBlock = block.number; } // 用户质押LP参与挖矿 function stake(uint lpAmount) external { settlePendingReward(); stakingLp.transferFrom(msg.sender, address(this), lpAmount); userStaked[msg.sender] += lpAmount; totalStakedLp += lpAmount; } // 结算待发放奖励的内部逻辑 function settlePendingReward() internal { if (block.number <= lastUpdateBlock || totalStakedLp == 0) { lastUpdateBlock = block.number; return; } uint totalPendingReward = (block.number - lastUpdateBlock) * rewardPerBlock; accRewardPerLp += totalPendingReward * 1e12 / totalStakedLp; lastUpdateBlock = block.number; } // 提取奖励、赎回LP的逻辑可以根据测试需求自行补充,测试阶段不需要做太复杂的安全校验 }
测试注意事项
- 本地调试优先用Hardhat/Foundry的主网fork模式,不需要自己部署Uniswap全套核心合约,直接用主网已部署的Factory、Router地址调用即可,能省很多部署步骤
- 默认生成的LP名称、符号是Uniswap预设格式,如果要自定义LP名称、图标,要么修改Factory合约源码重新部署,要么测试阶段自己写个Mock LP合约模拟逻辑即可,不影响挖矿流程测试
- 所有调试操作请在本地测试节点或公开测试网完成,不要直接在主网交互,避免不必要的资产损失
内容的提问来源于stack exchange,提问作者code_learner93
相关产品推荐
相关产品推荐

