Implement DeFi protocols with production-ready templates for staking, AMMs, governance, and lending systems. Use when building decentralized finance applications or smart contract protocols.
Add this skill
npx mdskills install sickn33/defi-protocol-templatesProvides comprehensive, production-ready Solidity templates for major DeFi protocols with security patterns
1---2name: defi-protocol-templates3description: Implement DeFi protocols with production-ready templates for staking, AMMs, governance, and lending systems. Use when building decentralized finance applications or smart contract protocols.4---56# DeFi Protocol Templates78Production-ready templates for common DeFi protocols including staking, AMMs, governance, lending, and flash loans.910## Do not use this skill when1112- The task is unrelated to defi protocol templates13- You need a different domain or tool outside this scope1415## Instructions1617- Clarify goals, constraints, and required inputs.18- Apply relevant best practices and validate outcomes.19- Provide actionable steps and verification.20- If detailed examples are required, open `resources/implementation-playbook.md`.2122## Use this skill when2324- Building staking platforms with reward distribution25- Implementing AMM (Automated Market Maker) protocols26- Creating governance token systems27- Developing lending/borrowing protocols28- Integrating flash loan functionality29- Launching yield farming platforms3031## Staking Contract3233```solidity34// SPDX-License-Identifier: MIT35pragma solidity ^0.8.0;3637import "@openzeppelin/contracts/token/ERC20/IERC20.sol";38import "@openzeppelin/contracts/security/ReentrancyGuard.sol";39import "@openzeppelin/contracts/access/Ownable.sol";4041contract StakingRewards is ReentrancyGuard, Ownable {42 IERC20 public stakingToken;43 IERC20 public rewardsToken;4445 uint256 public rewardRate = 100; // Rewards per second46 uint256 public lastUpdateTime;47 uint256 public rewardPerTokenStored;4849 mapping(address => uint256) public userRewardPerTokenPaid;50 mapping(address => uint256) public rewards;51 mapping(address => uint256) public balances;5253 uint256 private _totalSupply;5455 event Staked(address indexed user, uint256 amount);56 event Withdrawn(address indexed user, uint256 amount);57 event RewardPaid(address indexed user, uint256 reward);5859 constructor(address _stakingToken, address _rewardsToken) {60 stakingToken = IERC20(_stakingToken);61 rewardsToken = IERC20(_rewardsToken);62 }6364 modifier updateReward(address account) {65 rewardPerTokenStored = rewardPerToken();66 lastUpdateTime = block.timestamp;6768 if (account != address(0)) {69 rewards[account] = earned(account);70 userRewardPerTokenPaid[account] = rewardPerTokenStored;71 }72 _;73 }7475 function rewardPerToken() public view returns (uint256) {76 if (_totalSupply == 0) {77 return rewardPerTokenStored;78 }79 return rewardPerTokenStored +80 ((block.timestamp - lastUpdateTime) * rewardRate * 1e18) / _totalSupply;81 }8283 function earned(address account) public view returns (uint256) {84 return (balances[account] *85 (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18 +86 rewards[account];87 }8889 function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {90 require(amount > 0, "Cannot stake 0");91 _totalSupply += amount;92 balances[msg.sender] += amount;93 stakingToken.transferFrom(msg.sender, address(this), amount);94 emit Staked(msg.sender, amount);95 }9697 function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {98 require(amount > 0, "Cannot withdraw 0");99 _totalSupply -= amount;100 balances[msg.sender] -= amount;101 stakingToken.transfer(msg.sender, amount);102 emit Withdrawn(msg.sender, amount);103 }104105 function getReward() public nonReentrant updateReward(msg.sender) {106 uint256 reward = rewards[msg.sender];107 if (reward > 0) {108 rewards[msg.sender] = 0;109 rewardsToken.transfer(msg.sender, reward);110 emit RewardPaid(msg.sender, reward);111 }112 }113114 function exit() external {115 withdraw(balances[msg.sender]);116 getReward();117 }118}119```120121## AMM (Automated Market Maker)122123```solidity124// SPDX-License-Identifier: MIT125pragma solidity ^0.8.0;126127import "@openzeppelin/contracts/token/ERC20/IERC20.sol";128129contract SimpleAMM {130 IERC20 public token0;131 IERC20 public token1;132133 uint256 public reserve0;134 uint256 public reserve1;135136 uint256 public totalSupply;137 mapping(address => uint256) public balanceOf;138139 event Mint(address indexed to, uint256 amount);140 event Burn(address indexed from, uint256 amount);141 event Swap(address indexed trader, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out);142143 constructor(address _token0, address _token1) {144 token0 = IERC20(_token0);145 token1 = IERC20(_token1);146 }147148 function addLiquidity(uint256 amount0, uint256 amount1) external returns (uint256 shares) {149 token0.transferFrom(msg.sender, address(this), amount0);150 token1.transferFrom(msg.sender, address(this), amount1);151152 if (totalSupply == 0) {153 shares = sqrt(amount0 * amount1);154 } else {155 shares = min(156 (amount0 * totalSupply) / reserve0,157 (amount1 * totalSupply) / reserve1158 );159 }160161 require(shares > 0, "Shares = 0");162 _mint(msg.sender, shares);163 _update(164 token0.balanceOf(address(this)),165 token1.balanceOf(address(this))166 );167168 emit Mint(msg.sender, shares);169 }170171 function removeLiquidity(uint256 shares) external returns (uint256 amount0, uint256 amount1) {172 uint256 bal0 = token0.balanceOf(address(this));173 uint256 bal1 = token1.balanceOf(address(this));174175 amount0 = (shares * bal0) / totalSupply;176 amount1 = (shares * bal1) / totalSupply;177178 require(amount0 > 0 && amount1 > 0, "Amount0 or amount1 = 0");179180 _burn(msg.sender, shares);181 _update(bal0 - amount0, bal1 - amount1);182183 token0.transfer(msg.sender, amount0);184 token1.transfer(msg.sender, amount1);185186 emit Burn(msg.sender, shares);187 }188189 function swap(address tokenIn, uint256 amountIn) external returns (uint256 amountOut) {190 require(tokenIn == address(token0) || tokenIn == address(token1), "Invalid token");191192 bool isToken0 = tokenIn == address(token0);193 (IERC20 tokenIn_, IERC20 tokenOut, uint256 resIn, uint256 resOut) = isToken0194 ? (token0, token1, reserve0, reserve1)195 : (token1, token0, reserve1, reserve0);196197 tokenIn_.transferFrom(msg.sender, address(this), amountIn);198199 // 0.3% fee200 uint256 amountInWithFee = (amountIn * 997) / 1000;201 amountOut = (resOut * amountInWithFee) / (resIn + amountInWithFee);202203 tokenOut.transfer(msg.sender, amountOut);204205 _update(206 token0.balanceOf(address(this)),207 token1.balanceOf(address(this))208 );209210 emit Swap(msg.sender, isToken0 ? amountIn : 0, isToken0 ? 0 : amountIn, isToken0 ? 0 : amountOut, isToken0 ? amountOut : 0);211 }212213 function _mint(address to, uint256 amount) private {214 balanceOf[to] += amount;215 totalSupply += amount;216 }217218 function _burn(address from, uint256 amount) private {219 balanceOf[from] -= amount;220 totalSupply -= amount;221 }222223 function _update(uint256 res0, uint256 res1) private {224 reserve0 = res0;225 reserve1 = res1;226 }227228 function sqrt(uint256 y) private pure returns (uint256 z) {229 if (y > 3) {230 z = y;231 uint256 x = y / 2 + 1;232 while (x < z) {233 z = x;234 x = (y / x + x) / 2;235 }236 } else if (y != 0) {237 z = 1;238 }239 }240241 function min(uint256 x, uint256 y) private pure returns (uint256) {242 return x <= y ? x : y;243 }244}245```246247## Governance Token248249```solidity250// SPDX-License-Identifier: MIT251pragma solidity ^0.8.0;252253import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";254import "@openzeppelin/contracts/access/Ownable.sol";255256contract GovernanceToken is ERC20Votes, Ownable {257 constructor() ERC20("Governance Token", "GOV") ERC20Permit("Governance Token") {258 _mint(msg.sender, 1000000 * 10**decimals());259 }260261 function _afterTokenTransfer(262 address from,263 address to,264 uint256 amount265 ) internal override(ERC20Votes) {266 super._afterTokenTransfer(from, to, amount);267 }268269 function _mint(address to, uint256 amount) internal override(ERC20Votes) {270 super._mint(to, amount);271 }272273 function _burn(address account, uint256 amount) internal override(ERC20Votes) {274 super._burn(account, amount);275 }276}277278contract Governor is Ownable {279 GovernanceToken public governanceToken;280281 struct Proposal {282 uint256 id;283 address proposer;284 string description;285 uint256 forVotes;286 uint256 againstVotes;287 uint256 startBlock;288 uint256 endBlock;289 bool executed;290 mapping(address => bool) hasVoted;291 }292293 uint256 public proposalCount;294 mapping(uint256 => Proposal) public proposals;295296 uint256 public votingPeriod = 17280; // ~3 days in blocks297 uint256 public proposalThreshold = 100000 * 10**18;298299 event ProposalCreated(uint256 indexed proposalId, address proposer, string description);300 event VoteCast(address indexed voter, uint256 indexed proposalId, bool support, uint256 weight);301 event ProposalExecuted(uint256 indexed proposalId);302303 constructor(address _governanceToken) {304 governanceToken = GovernanceToken(_governanceToken);305 }306307 function propose(string memory description) external returns (uint256) {308 require(309 governanceToken.getPastVotes(msg.sender, block.number - 1) >= proposalThreshold,310 "Proposer votes below threshold"311 );312313 proposalCount++;314 Proposal storage newProposal = proposals[proposalCount];315 newProposal.id = proposalCount;316 newProposal.proposer = msg.sender;317 newProposal.description = description;318 newProposal.startBlock = block.number;319 newProposal.endBlock = block.number + votingPeriod;320321 emit ProposalCreated(proposalCount, msg.sender, description);322 return proposalCount;323 }324325 function vote(uint256 proposalId, bool support) external {326 Proposal storage proposal = proposals[proposalId];327 require(block.number >= proposal.startBlock, "Voting not started");328 require(block.number <= proposal.endBlock, "Voting ended");329 require(!proposal.hasVoted[msg.sender], "Already voted");330331 uint256 weight = governanceToken.getPastVotes(msg.sender, proposal.startBlock);332 require(weight > 0, "No voting power");333334 proposal.hasVoted[msg.sender] = true;335336 if (support) {337 proposal.forVotes += weight;338 } else {339 proposal.againstVotes += weight;340 }341342 emit VoteCast(msg.sender, proposalId, support, weight);343 }344345 function execute(uint256 proposalId) external {346 Proposal storage proposal = proposals[proposalId];347 require(block.number > proposal.endBlock, "Voting not ended");348 require(!proposal.executed, "Already executed");349 require(proposal.forVotes > proposal.againstVotes, "Proposal failed");350351 proposal.executed = true;352353 // Execute proposal logic here354355 emit ProposalExecuted(proposalId);356 }357}358```359360## Flash Loan361362```solidity363// SPDX-License-Identifier: MIT364pragma solidity ^0.8.0;365366import "@openzeppelin/contracts/token/ERC20/IERC20.sol";367368interface IFlashLoanReceiver {369 function executeOperation(370 address asset,371 uint256 amount,372 uint256 fee,373 bytes calldata params374 ) external returns (bool);375}376377contract FlashLoanProvider {378 IERC20 public token;379 uint256 public feePercentage = 9; // 0.09% fee380381 event FlashLoan(address indexed borrower, uint256 amount, uint256 fee);382383 constructor(address _token) {384 token = IERC20(_token);385 }386387 function flashLoan(388 address receiver,389 uint256 amount,390 bytes calldata params391 ) external {392 uint256 balanceBefore = token.balanceOf(address(this));393 require(balanceBefore >= amount, "Insufficient liquidity");394395 uint256 fee = (amount * feePercentage) / 10000;396397 // Send tokens to receiver398 token.transfer(receiver, amount);399400 // Execute callback401 require(402 IFlashLoanReceiver(receiver).executeOperation(403 address(token),404 amount,405 fee,406 params407 ),408 "Flash loan failed"409 );410411 // Verify repayment412 uint256 balanceAfter = token.balanceOf(address(this));413 require(balanceAfter >= balanceBefore + fee, "Flash loan not repaid");414415 emit FlashLoan(receiver, amount, fee);416 }417}418419// Example flash loan receiver420contract FlashLoanReceiver is IFlashLoanReceiver {421 function executeOperation(422 address asset,423 uint256 amount,424 uint256 fee,425 bytes calldata params426 ) external override returns (bool) {427 // Decode params and execute arbitrage, liquidation, etc.428 // ...429430 // Approve repayment431 IERC20(asset).approve(msg.sender, amount + fee);432433 return true;434 }435}436```437438## Resources439440- **references/staking.md**: Staking mechanics and reward distribution441- **references/liquidity-pools.md**: AMM mathematics and pricing442- **references/governance-tokens.md**: Governance and voting systems443- **references/lending-protocols.md**: Lending/borrowing implementation444- **references/flash-loans.md**: Flash loan security and use cases445- **assets/staking-contract.sol**: Production staking template446- **assets/amm-contract.sol**: Full AMM implementation447- **assets/governance-token.sol**: Governance system448- **assets/lending-protocol.sol**: Lending platform template449450## Best Practices4514521. **Use Established Libraries**: OpenZeppelin, Solmate4532. **Test Thoroughly**: Unit tests, integration tests, fuzzing4543. **Audit Before Launch**: Professional security audits4554. **Start Simple**: MVP first, add features incrementally4565. **Monitor**: Track contract health and user activity4576. **Upgradability**: Consider proxy patterns for upgrades4587. **Emergency Controls**: Pause mechanisms for critical issues459460## Common DeFi Patterns461462- **Time-Weighted Average Price (TWAP)**: Price oracle resistance463- **Liquidity Mining**: Incentivize liquidity provision464- **Vesting**: Lock tokens with gradual release465- **Multisig**: Require multiple signatures for critical operations466- **Timelocks**: Delay execution of governance decisions467
Full transparency — inspect the skill content before installing.