defi-protocol-templates

利用生产就绪的模板实现DeFi协议,涵盖质押、自动化做市商(AMM)、治理和借贷系统。适用于构建去中心化金融应用或智能合约协议的场景。

查看详情
name:defi-protocol-templatesdescription:Implement DeFi protocols with production-ready templates for staking, AMMs, governance, and lending systems. Use when building decentralized finance applications or smart contract protocols.

DeFi Protocol Templates

Production-ready templates for common DeFi protocols including staking, AMMs, governance, lending, and flash loans.

Do not use this skill when

  • The task is unrelated to defi protocol templates

  • You need a different domain or tool outside this scope
  • Instructions

  • Clarify goals, constraints, and required inputs.

  • Apply relevant best practices and validate outcomes.

  • Provide actionable steps and verification.

  • If detailed examples are required, open resources/implementation-playbook.md.
  • Use this skill when

  • Building staking platforms with reward distribution

  • Implementing AMM (Automated Market Maker) protocols

  • Creating governance token systems

  • Developing lending/borrowing protocols

  • Integrating flash loan functionality

  • Launching yield farming platforms
  • Staking Contract

    // 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 (Automated Market Maker)

    // 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");
    _mint(msg.sender, shares);
    _update(
    token0.balanceOf(address(this)),
    token1.balanceOf(address(this))
    );

    emit Mint(msg.sender, shares);
    }

    function removeLiquidity(uint256 shares) external returns (uint256 amount0, uint256 amount1) {
    uint256 bal0 = token0.balanceOf(address(this));
    uint256 bal1 = token1.balanceOf(address(this));

    amount0 = (shares bal0) / totalSupply;
    amount1 = (shares
    bal1) / totalSupply;

    require(amount0 > 0 && amount1 > 0, "Amount0 or amount1 = 0");

    _burn(msg.sender, shares);
    _update(bal0 - amount0, bal1 - amount1);

    token0.transfer(msg.sender, amount0);
    token1.transfer(msg.sender, amount1);

    emit Burn(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 tokenIn_, IERC20 tokenOut, uint256 resIn, uint256 resOut) = isToken0
    ? (token0, token1, reserve0, reserve1)
    : (token1, token0, reserve1, reserve0);

    tokenIn_.transferFrom(msg.sender, address(this), amountIn);

    // 0.3% fee
    uint256 amountInWithFee = (amountIn 997) / 1000;
    amountOut = (resOut
    amountInWithFee) / (resIn + amountInWithFee);

    tokenOut.transfer(msg.sender, amountOut);

    _update(
    token0.balanceOf(address(this)),
    token1.balanceOf(address(this))
    );

    emit Swap(msg.sender, isToken0 ? amountIn : 0, isToken0 ? 0 : amountIn, isToken0 ? 0 : amountOut, isToken0 ? amountOut : 0);
    }

    function _mint(address to, uint256 amount) private {
    balanceOf[to] += amount;
    totalSupply += amount;
    }

    function _burn(address from, uint256 amount) private {
    balanceOf[from] -= amount;
    totalSupply -= amount;
    }

    function _update(uint256 res0, uint256 res1) private {
    reserve0 = res0;
    reserve1 = res1;
    }

    function sqrt(uint256 y) private 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) private pure returns (uint256) {
    return x <= y ? x : y;
    }
    }

    Governance Token

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;

    import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
    import "@openzeppelin/contracts/access/Ownable.sol";

    contract GovernanceToken is ERC20Votes, Ownable {
    constructor() ERC20("Governance Token", "GOV") ERC20Permit("Governance Token") {
    _mint(msg.sender, 1000000 10decimals());
    }

    function _afterTokenTransfer(
    address from,
    address to,
    uint256 amount
    ) internal override(ERC20Votes) {
    super._afterTokenTransfer(from, to, amount);
    }

    function _mint(address to, uint256 amount) internal override(ERC20Votes) {
    super._mint(to, amount);
    }

    function _burn(address account, uint256 amount) internal override(ERC20Votes) {
    super._burn(account, amount);
    }
    }

    contract Governor is Ownable {
    GovernanceToken public governanceToken;

    struct Proposal {
    uint256 id;
    address proposer;
    string description;
    uint256 forVotes;
    uint256 againstVotes;
    uint256 startBlock;
    uint256 endBlock;
    bool executed;
    mapping(address => bool) hasVoted;
    }

    uint256 public proposalCount;
    mapping(uint256 => Proposal) public proposals;

    uint256 public votingPeriod = 17280; // ~3 days in blocks
    uint256 public proposalThreshold = 100000
    10*18;

    event ProposalCreated(uint256 indexed proposalId, address proposer, string description);
    event VoteCast(address indexed voter, uint256 indexed proposalId, bool support, uint256 weight);
    event ProposalExecuted(uint256 indexed proposalId);

    constructor(address _governanceToken) {
    governanceToken = GovernanceToken(_governanceToken);
    }

    function propose(string memory description) external returns (uint256) {
    require(
    governanceToken.getPastVotes(msg.sender, block.number - 1) >= proposalThreshold,
    "Proposer votes below threshold"
    );

    proposalCount++;
    Proposal storage newProposal = proposals[proposalCount];
    newProposal.id = proposalCount;
    newProposal.proposer = msg.sender;
    newProposal.description = description;
    newProposal.startBlock = block.number;
    newProposal.endBlock = block.number + votingPeriod;

    emit ProposalCreated(proposalCount, msg.sender, description);
    return proposalCount;
    }

    function vote(uint256 proposalId, bool support) external {
    Proposal storage proposal = proposals[proposalId];
    require(block.number >= proposal.startBlock, "Voting not started");
    require(block.number <= proposal.endBlock, "Voting ended");
    require(!proposal.hasVoted[msg.sender], "Already voted");

    uint256 weight = governanceToken.getPastVotes(msg.sender, proposal.startBlock);
    require(weight > 0, "No voting power");

    proposal.hasVoted[msg.sender] = true;

    if (support) {
    proposal.forVotes += weight;
    } else {
    proposal.againstVotes += weight;
    }

    emit VoteCast(msg.sender, proposalId, support, weight);
    }

    function execute(uint256 proposalId) external {
    Proposal storage proposal = proposals[proposalId];
    require(block.number > proposal.endBlock, "Voting not ended");
    require(!proposal.executed, "Already executed");
    require(proposal.forVotes > proposal.againstVotes, "Proposal failed");

    proposal.executed = true;

    // Execute proposal logic here

    emit ProposalExecuted(proposalId);
    }
    }

    Flash Loan

    // SPDX-License-Identifier: MIT
    pragma solidity ^0.8.0;

    import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

    interface IFlashLoanReceiver {
    function executeOperation(
    address asset,
    uint256 amount,
    uint256 fee,
    bytes calldata params
    ) external returns (bool);
    }

    contract FlashLoanProvider {
    IERC20 public token;
    uint256 public feePercentage = 9; // 0.09% fee

    event FlashLoan(address indexed borrower, uint256 amount, uint256 fee);

    constructor(address _token) {
    token = IERC20(_token);
    }

    function flashLoan(
    address receiver,
    uint256 amount,
    bytes calldata params
    ) external {
    uint256 balanceBefore = token.balanceOf(address(this));
    require(balanceBefore >= amount, "Insufficient liquidity");

    uint256 fee = (amount feePercentage) / 10000;

    // Send tokens to receiver
    token.transfer(receiver, amount);

    // Execute callback
    require(
    IFlashLoanReceiver(receiver).executeOperation(
    address(token),
    amount,
    fee,
    params
    ),
    "Flash loan failed"
    );

    // Verify repayment
    uint256 balanceAfter = token.balanceOf(address(this));
    require(balanceAfter >= balanceBefore + fee, "Flash loan not repaid");

    emit FlashLoan(receiver, amount, fee);
    }
    }

    // Example flash loan receiver
    contract FlashLoanReceiver is IFlashLoanReceiver {
    function executeOperation(
    address asset,
    uint256 amount,
    uint256 fee,
    bytes calldata params
    ) external override returns (bool) {
    // Decode params and execute arbitrage, liquidation, etc.
    // ...

    // Approve repayment
    IERC20(asset).approve(msg.sender, amount + fee);

    return true;
    }
    }

    Resources

  • references/staking.md: Staking mechanics and reward distribution

  • references/liquidity-pools.md: AMM mathematics and pricing

  • references/governance-tokens.md: Governance and voting systems

  • references/lending-protocols.md: Lending/borrowing implementation

  • references/flash-loans.md: Flash loan security and use cases

  • assets/staking-contract.sol: Production staking template

  • assets/amm-contract.sol: Full AMM implementation

  • assets/governance-token.sol: Governance system

  • assets/lending-protocol.sol: Lending platform template
  • Best Practices

  • Use Established Libraries: OpenZeppelin, Solmate

  • Test Thoroughly: Unit tests, integration tests, fuzzing

  • Audit Before Launch: Professional security audits

  • Start Simple: MVP first, add features incrementally

  • Monitor: Track contract health and user activity

  • Upgradability: Consider proxy patterns for upgrades

  • Emergency Controls: Pause mechanisms for critical issues
  • Common DeFi Patterns

  • Time-Weighted Average Price (TWAP): Price oracle resistance

  • Liquidity Mining: Incentivize liquidity provision

  • Vesting: Lock tokens with gradual release

  • Multisig: Require multiple signatures for critical operations

  • Timelocks: Delay execution of governance decisions