
关于
使用生产就绪模板实现 DeFi 协议,涵盖质押、AMM、治理和借贷系统。
name: defi-protocol-templates description: "使用生产级模板实现 DeFi 协议,涵盖质押、AMM、治理和借贷系统。适用于构建去中心化金融应用或智能合约协议。" risk: unknown source: community date_added: "2026-02-27"
DeFi 协议模板
涵盖质押、AMM、治理、借贷和闪电贷的生产级 DeFi 协议模板。
不适用场景
- 任务与 DeFi 协议模板无关
- 需要其他领域或工具
使用说明
- 明确目标、约束条件和所需输入
- 应用最佳实践并验证结果
- 提供可操作步骤和验证方法
- 如需详细示例,请打开
resources/implementation-playbook.md
适用场景
- 构建带奖励分配的质押平台
- 实现 AMM(自动做市商)协议
- 创建治理代币系统
- 开发借贷协议
- 集成闪电贷功能
- 启动流动性挖矿平台
质押合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract StakingRewards is ReentrancyGuard, Ownable {
IERC20 public stakingToken;
IERC20 public rewardsToken;
uint256 public rewardRate = 100; // Rewards per second
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
mapping(address => uint256) public balances;
uint256 private _totalSupply;
event Staked(address indexed user, uint256 amount);
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward);
constructor(address _stakingToken, address _rewardsToken) {
stakingToken = IERC20(_stakingToken);
rewardsToken = IERC20(_rewardsToken);
}
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = block.timestamp;
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
function rewardPerToken() public view returns (uint256) {
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored +
((block.timestamp - lastUpdateTime) * rewardRate * 1e18) / _totalSupply;
}
function earned(address account) public view returns (uint256) {
return (balances[account] *
(rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 +
rewards[account];
}
function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot stake 0");
_totalSupply += amount;
balances[msg.sender] += amount;
stakingToken.transferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot withdraw 0");
_totalSupply -= amount;
balances[msg.sender] -= amount;
stakingToken.transfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function getReward() public nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
rewardsToken.transfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
function exit() external {
withdraw(balances[msg.sender]);
getReward();
}
}
AMM(自动做市商)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract SimpleAMM {
IERC20 public token0;
IERC20 public token1;
uint256 public reserve0;
uint256 public reserve1;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
event Mint(address indexed to, uint256 amount);
event Burn(address indexed from, uint256 amount);
event Swap(address indexed trader, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out);
constructor(address _token0, address _token1) {
token0 = IERC20(_token0);
token1 = IERC20(_token1);
}
function addLiquidity(uint256 amount0, uint256 amount1) external returns (uint256 shares) {
token0.transferFrom(msg.sender, address(this), amount0);
token1.transferFrom(msg.sender, address(this), amount1);
if (totalSupply == 0) {
shares = sqrt(amount0 * amount1);
} else {
shares = min(
(amount0 * totalSupply) / reserve0,
(amount1 * totalSupply) / reserve1
);
}
require(shares > 0, "Shares = 0");
balanceOf[msg.sender] += shares;
totalSupply += shares;
reserve0 += amount0;
reserve1 += amount1;
emit Mint(msg.sender, shares);
}
function swap(address tokenIn, uint256 amountIn) external returns (uint256 amountOut) {
require(tokenIn == address(token0) || tokenIn == address(token1), "Invalid token");
bool isToken0 = tokenIn == address(token0);
(IERC20 input, IERC20 output, uint256 resIn, uint256 resOut) = isToken0
? (token0, token1, reserve0, reserve1)
: (token1, token0, reserve1, reserve0);
input.transferFrom(msg.sender, address(this), amountIn);
uint256 amountInWithFee = (amountIn * 997) / 1000; // 0.3% fee
amountOut = (resOut * amountInWithFee) / (resIn + amountInWithFee);
output.transfer(msg.sender, amountOut);
if (isToken0) { reserve0 += amountIn; reserve1 -= amountOut; }
else { reserve1 += amountIn; reserve0 -= amountOut; }
}
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } }
else if (y != 0) { z = 1; }
}
function min(uint256 x, uint256 y) internal pure returns (uint256) { return x <= y ? x : y; }
}
安全最佳实践
- 使用 ReentrancyGuard 防止重入攻击
- 实现紧急暂停机制(Pausable)
- 添加时间锁用于治理操作
- 使用 SafeERC20 进行代币转账
- 部署前进行全面审计
- 实现速率限制和提款上限
兼容工具
Claude CodeCursor
标签
后端开发
