Skip to content

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.

10,472
installs
0 up / 0 down, score 0.00
votes
41,467
repo stars
Updated
less than a month ago
In the catalog since
1 Oct 2026
From
wshobson/agents
Get it through your agent npx skills add wshobson/agents@defi-protocol-templates

Skill instructions

Rendered from the published SKILL.md, 232 lines.

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);
Show the whole skill (112 more lines)
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.