Claude Cursor Skill

defi-protocol-templates

Implement DeFi protocols with production-ready templates for staking, AMMs, governance, and flash loans. Use when building decentralized finance applications or smart contract protocols.

LLM Mart · 0 points · 14 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download wshobson-agents-plugins_blockchain-web3_skills_defi-protocol-templates-554237f.zip · 3 KB
Part of wshobson/agents — 170 skills

Install

skills CLI npx skills add https://github.com/wshobson/agents/tree/main/plugins/blockchain-web3/skills/defi-protocol-templates
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wshobson-agents@llmmart
Git git clone https://github.com/wshobson/agents.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wshobson/agents collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

DeFi Protocol Templates

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

When to Use This Skill

  • Building staking platforms with reward distribution
  • Implementing AMM (Automated Market Maker) protocols
  • Creating governance token systems
  • Integrating flash loan functionality

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;
    }
}

Additional patterns and templates

More detailed templates and worked examples live in references/details.md. Read that file for the full pattern library.

Files (agents)
  • references
    • details.md 5.5 KB
      # defi-protocol-templates — additional patterns and templates
      
      ## Governance Token
      
      ```solidity
      // 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 * 10**decimals());
          }
      
          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
      
      ```solidity
      // 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;
          }
      }
      ```
      
  • SKILL.md 7.3 KB
    ---
    name: defi-protocol-templates
    description: Implement DeFi protocols with production-ready templates for staking, AMMs, governance, and flash loans. Use when building decentralized finance applications or smart contract protocols.
    ---
    
    # DeFi Protocol Templates
    
    Production-ready templates for common DeFi protocols including staking, AMMs, governance, and flash loans.
    
    ## When to Use This Skill
    
    - Building staking platforms with reward distribution
    - Implementing AMM (Automated Market Maker) protocols
    - Creating governance token systems
    - Integrating flash loan functionality
    
    ## Staking Contract
    
    ```solidity
    // 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)
    
    ```solidity
    // 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;
        }
    }
    ```
    
    ## Additional patterns and templates
    
    More detailed templates and worked examples live in `references/details.md`. Read that file for the full pattern library.
    
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related