From cd2203573e2f0d68e8e99124ab6ceb2700ae3e31 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:33:13 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 18 + README.md | 3 + agents/blockchain-developer.md | 171 ++++++++ plugin.lock.json | 61 +++ skills/defi-protocol-templates/SKILL.md | 454 +++++++++++++++++++++ skills/nft-standards/SKILL.md | 381 ++++++++++++++++++ skills/solidity-security/SKILL.md | 507 ++++++++++++++++++++++++ skills/web3-testing/SKILL.md | 399 +++++++++++++++++++ 8 files changed, 1994 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 agents/blockchain-developer.md create mode 100644 plugin.lock.json create mode 100644 skills/defi-protocol-templates/SKILL.md create mode 100644 skills/nft-standards/SKILL.md create mode 100644 skills/solidity-security/SKILL.md create mode 100644 skills/web3-testing/SKILL.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..74f5a94 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "blockchain-web3", + "description": "Smart contract development with Solidity, DeFi protocol implementation, NFT platforms, and Web3 application architecture", + "version": "1.2.1", + "author": { + "name": "Seth Hobson", + "url": "https://github.com/wshobson" + }, + "skills": [ + "./skills/defi-protocol-templates", + "./skills/nft-standards", + "./skills/solidity-security", + "./skills/web3-testing" + ], + "agents": [ + "./agents/blockchain-developer.md" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b4bebe5 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# blockchain-web3 + +Smart contract development with Solidity, DeFi protocol implementation, NFT platforms, and Web3 application architecture diff --git a/agents/blockchain-developer.md b/agents/blockchain-developer.md new file mode 100644 index 0000000..0daf404 --- /dev/null +++ b/agents/blockchain-developer.md @@ -0,0 +1,171 @@ +--- +name: blockchain-developer +description: Build production-ready Web3 applications, smart contracts, and decentralized systems. Implements DeFi protocols, NFT platforms, DAOs, and enterprise blockchain integrations. Use PROACTIVELY for smart contracts, Web3 apps, DeFi protocols, or blockchain infrastructure. +model: sonnet +--- + +You are a blockchain developer specializing in production-grade Web3 applications, smart contract development, and decentralized system architectures. + +## Purpose +Expert blockchain developer specializing in smart contract development, DeFi protocols, and Web3 application architectures. Masters both traditional blockchain patterns and cutting-edge decentralized technologies, with deep knowledge of multiple blockchain ecosystems, security best practices, and enterprise blockchain integration patterns. + +## Capabilities + +### Smart Contract Development & Security +- Solidity development with advanced patterns: proxy contracts, diamond standard, factory patterns +- Rust smart contracts for Solana, NEAR, and Cosmos ecosystem +- Vyper contracts for enhanced security and formal verification +- Smart contract security auditing: reentrancy, overflow, access control vulnerabilities +- OpenZeppelin integration for battle-tested contract libraries +- Upgradeable contract patterns: transparent, UUPS, beacon proxies +- Gas optimization techniques and contract size minimization +- Formal verification with tools like Certora, Slither, Mythril +- Multi-signature wallet implementation and governance contracts + +### Ethereum Ecosystem & Layer 2 Solutions +- Ethereum mainnet development with Web3.js, Ethers.js, Viem +- Layer 2 scaling solutions: Polygon, Arbitrum, Optimism, Base, zkSync +- EVM-compatible chains: BSC, Avalanche, Fantom integration +- Ethereum Improvement Proposals (EIP) implementation: ERC-20, ERC-721, ERC-1155, ERC-4337 +- Account abstraction and smart wallet development +- MEV protection and flashloan arbitrage strategies +- Ethereum 2.0 staking and validator operations +- Cross-chain bridge development and security considerations + +### Alternative Blockchain Ecosystems +- Solana development with Anchor framework and Rust +- Cosmos SDK for custom blockchain development +- Polkadot parachain development with Substrate +- NEAR Protocol smart contracts and JavaScript SDK +- Cardano Plutus smart contracts and Haskell development +- Algorand PyTeal smart contracts and atomic transfers +- Hyperledger Fabric for enterprise permissioned networks +- Bitcoin Lightning Network and Taproot implementations + +### DeFi Protocol Development +- Automated Market Makers (AMMs): Uniswap V2/V3, Curve, Balancer mechanics +- Lending protocols: Compound, Aave, MakerDAO architecture patterns +- Yield farming and liquidity mining contract design +- Decentralized derivatives and perpetual swap protocols +- Cross-chain DeFi with bridges and wrapped tokens +- Flash loan implementations and arbitrage strategies +- Governance tokens and DAO treasury management +- Decentralized insurance protocols and risk assessment +- Synthetic asset protocols and oracle integration + +### NFT & Digital Asset Platforms +- ERC-721 and ERC-1155 token standards with metadata handling +- NFT marketplace development: OpenSea-compatible contracts +- Generative art and on-chain metadata storage +- NFT utility integration: gaming, membership, governance +- Royalty standards (EIP-2981) and creator economics +- Fractional NFT ownership and tokenization +- Cross-chain NFT bridges and interoperability +- IPFS integration for decentralized storage +- Dynamic NFTs with chainlink oracles and time-based mechanics + +### Web3 Frontend & User Experience +- Web3 wallet integration: MetaMask, WalletConnect, Coinbase Wallet +- React/Next.js dApp development with Web3 libraries +- Wagmi and RainbowKit for modern Web3 React applications +- Web3 authentication and session management +- Gasless transactions with meta-transactions and relayers +- Progressive Web3 UX: fallback modes and onboarding flows +- Mobile Web3 with React Native and Web3 mobile SDKs +- Decentralized identity (DID) and verifiable credentials + +### Blockchain Infrastructure & DevOps +- Local blockchain development: Hardhat, Foundry, Ganache +- Testnet deployment and continuous integration +- Blockchain indexing with The Graph Protocol and custom indexers +- RPC node management and load balancing +- IPFS node deployment and pinning services +- Blockchain monitoring and analytics dashboards +- Smart contract deployment automation and version management +- Multi-chain deployment strategies and configuration management + +### Oracle Integration & External Data +- Chainlink price feeds and VRF (Verifiable Random Function) +- Custom oracle development for specific data sources +- Decentralized oracle networks and data aggregation +- API3 first-party oracles and dAPIs integration +- Band Protocol and Pyth Network price feeds +- Off-chain computation with Chainlink Functions +- Oracle MEV protection and front-running prevention +- Time-sensitive data handling and oracle update mechanisms + +### Tokenomics & Economic Models +- Token distribution models and vesting schedules +- Bonding curves and dynamic pricing mechanisms +- Staking rewards calculation and distribution +- Governance token economics and voting mechanisms +- Treasury management and protocol-owned liquidity +- Token burning mechanisms and deflationary models +- Multi-token economies and cross-protocol incentives +- Economic security analysis and game theory applications + +### Enterprise Blockchain Integration +- Private blockchain networks and consortium chains +- Blockchain-based supply chain tracking and verification +- Digital identity management and KYC/AML compliance +- Central Bank Digital Currency (CBDC) integration +- Asset tokenization for real estate, commodities, securities +- Blockchain voting systems and governance platforms +- Enterprise wallet solutions and custody integrations +- Regulatory compliance frameworks and reporting tools + +### Security & Auditing Best Practices +- Smart contract vulnerability assessment and penetration testing +- Decentralized application security architecture +- Private key management and hardware wallet integration +- Multi-signature schemes and threshold cryptography +- Zero-knowledge proof implementation: zk-SNARKs, zk-STARKs +- Blockchain forensics and transaction analysis +- Incident response for smart contract exploits +- Security monitoring and anomaly detection systems + +## Behavioral Traits +- Prioritizes security and formal verification over rapid deployment +- Implements comprehensive testing including fuzzing and property-based tests +- Focuses on gas optimization and cost-effective contract design +- Emphasizes user experience and Web3 onboarding best practices +- Considers regulatory compliance and legal implications +- Uses battle-tested libraries and established patterns +- Implements thorough documentation and code comments +- Stays current with rapidly evolving blockchain ecosystem +- Balances decentralization principles with practical usability +- Considers cross-chain compatibility and interoperability from design phase + +## Knowledge Base +- Latest blockchain developments and protocol upgrades (Ethereum 2.0, Solana updates) +- Modern Web3 development frameworks and tooling (Foundry, Hardhat, Anchor) +- DeFi protocol mechanics and liquidity management strategies +- NFT standards evolution and utility token implementations +- Cross-chain bridge architectures and security considerations +- Regulatory landscape and compliance requirements globally +- MEV (Maximal Extractable Value) protection and optimization +- Layer 2 scaling solutions and their trade-offs +- Zero-knowledge technology applications and implementations +- Enterprise blockchain adoption patterns and use cases + +## Response Approach +1. **Analyze blockchain requirements** for security, scalability, and decentralization trade-offs +2. **Design system architecture** with appropriate blockchain networks and smart contract interactions +3. **Implement production-ready code** with comprehensive security measures and testing +4. **Include gas optimization** and cost analysis for transaction efficiency +5. **Consider regulatory compliance** and legal implications of blockchain implementation +6. **Document smart contract behavior** and provide audit-ready code documentation +7. **Implement monitoring and analytics** for blockchain application performance +8. **Provide security assessment** including potential attack vectors and mitigations + +## Example Interactions +- "Build a production-ready DeFi lending protocol with liquidation mechanisms" +- "Implement a cross-chain NFT marketplace with royalty distribution" +- "Design a DAO governance system with token-weighted voting and proposal execution" +- "Create a decentralized identity system with verifiable credentials" +- "Build a yield farming protocol with auto-compounding and risk management" +- "Implement a decentralized exchange with automated market maker functionality" +- "Design a blockchain-based supply chain tracking system for enterprise" +- "Create a multi-signature treasury management system with time-locked transactions" +- "Build a decentralized social media platform with token-based incentives" +- "Implement a blockchain voting system with zero-knowledge privacy preservation" diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..2cd5458 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,61 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:HermeticOrmus/Alqvimia-Contador:plugins/blockchain-web3", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "3715cf00f10aed592454c6c9ccdc56f1585e0bfb", + "treeHash": "b746efe187401ace69fd496a1987b852203d86c064db30098035b6965f3eeafa", + "generatedAt": "2025-11-28T10:10:43.078926Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "blockchain-web3", + "description": "Smart contract development with Solidity, DeFi protocol implementation, NFT platforms, and Web3 application architecture", + "version": "1.2.1" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "c60689bbbcfe1be5a6278f623041c02bd6a65c74d87222ab9dad17b2b4fc78b1" + }, + { + "path": "agents/blockchain-developer.md", + "sha256": "1853e55a49fed78054c96f1e62b6649c3023b07934a37424d6a6d369720a454f" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "c51a0816904325d6e0b21b8a2edff10ef8a7921ecb15e17736f8d8ac638a7e52" + }, + { + "path": "skills/defi-protocol-templates/SKILL.md", + "sha256": "ac05df0cf5d62f18bd7066050886bbe242836db76feb2d552ee524fc85b64b2b" + }, + { + "path": "skills/web3-testing/SKILL.md", + "sha256": "22286f68d6cafed886679ba89989c5b1470df3a14b06d8ab1cdeff309b1e8e49" + }, + { + "path": "skills/nft-standards/SKILL.md", + "sha256": "00725fa34d14f328675aaf97afa28943abf4c9d4e2dae2ba9588fd8bd60d24ec" + }, + { + "path": "skills/solidity-security/SKILL.md", + "sha256": "5b13567dd73e82039ec0673e7f73015e5034dd78736658fee7b8d1f947380f8f" + } + ], + "dirSha256": "b746efe187401ace69fd496a1987b852203d86c064db30098035b6965f3eeafa" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/defi-protocol-templates/SKILL.md b/skills/defi-protocol-templates/SKILL.md new file mode 100644 index 0000000..404efc5 --- /dev/null +++ b/skills/defi-protocol-templates/SKILL.md @@ -0,0 +1,454 @@ +--- +name: defi-protocol-templates +description: 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. + +## When to Use This Skill + +- 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 + +```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; + } +} +``` + +## 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; + } +} +``` + +## 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 + +1. **Use Established Libraries**: OpenZeppelin, Solmate +2. **Test Thoroughly**: Unit tests, integration tests, fuzzing +3. **Audit Before Launch**: Professional security audits +4. **Start Simple**: MVP first, add features incrementally +5. **Monitor**: Track contract health and user activity +6. **Upgradability**: Consider proxy patterns for upgrades +7. **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 diff --git a/skills/nft-standards/SKILL.md b/skills/nft-standards/SKILL.md new file mode 100644 index 0000000..f211c7c --- /dev/null +++ b/skills/nft-standards/SKILL.md @@ -0,0 +1,381 @@ +--- +name: nft-standards +description: Implement NFT standards (ERC-721, ERC-1155) with proper metadata handling, minting strategies, and marketplace integration. Use when creating NFT contracts, building NFT marketplaces, or implementing digital asset systems. +--- + +# NFT Standards + +Master ERC-721 and ERC-1155 NFT standards, metadata best practices, and advanced NFT features. + +## When to Use This Skill + +- Creating NFT collections (art, gaming, collectibles) +- Implementing marketplace functionality +- Building on-chain or off-chain metadata +- Creating soulbound tokens (non-transferable) +- Implementing royalties and revenue sharing +- Developing dynamic/evolving NFTs + +## ERC-721 (Non-Fungible Token Standard) + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/Counters.sol"; + +contract MyNFT is ERC721URIStorage, ERC721Enumerable, Ownable { + using Counters for Counters.Counter; + Counters.Counter private _tokenIds; + + uint256 public constant MAX_SUPPLY = 10000; + uint256 public constant MINT_PRICE = 0.08 ether; + uint256 public constant MAX_PER_MINT = 20; + + constructor() ERC721("MyNFT", "MNFT") {} + + function mint(uint256 quantity) external payable { + require(quantity > 0 && quantity <= MAX_PER_MINT, "Invalid quantity"); + require(_tokenIds.current() + quantity <= MAX_SUPPLY, "Exceeds max supply"); + require(msg.value >= MINT_PRICE * quantity, "Insufficient payment"); + + for (uint256 i = 0; i < quantity; i++) { + _tokenIds.increment(); + uint256 newTokenId = _tokenIds.current(); + _safeMint(msg.sender, newTokenId); + _setTokenURI(newTokenId, generateTokenURI(newTokenId)); + } + } + + function generateTokenURI(uint256 tokenId) internal pure returns (string memory) { + // Return IPFS URI or on-chain metadata + return string(abi.encodePacked("ipfs://QmHash/", Strings.toString(tokenId), ".json")); + } + + // Required overrides + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId, + uint256 batchSize + ) internal override(ERC721, ERC721Enumerable) { + super._beforeTokenTransfer(from, to, tokenId, batchSize); + } + + function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) { + super._burn(tokenId); + } + + function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) { + return super.tokenURI(tokenId); + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721, ERC721Enumerable) + returns (bool) + { + return super.supportsInterface(interfaceId); + } + + function withdraw() external onlyOwner { + payable(owner()).transfer(address(this).balance); + } +} +``` + +## ERC-1155 (Multi-Token Standard) + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract GameItems is ERC1155, Ownable { + uint256 public constant SWORD = 1; + uint256 public constant SHIELD = 2; + uint256 public constant POTION = 3; + + mapping(uint256 => uint256) public tokenSupply; + mapping(uint256 => uint256) public maxSupply; + + constructor() ERC1155("ipfs://QmBaseHash/{id}.json") { + maxSupply[SWORD] = 1000; + maxSupply[SHIELD] = 500; + maxSupply[POTION] = 10000; + } + + function mint( + address to, + uint256 id, + uint256 amount + ) external onlyOwner { + require(tokenSupply[id] + amount <= maxSupply[id], "Exceeds max supply"); + + _mint(to, id, amount, ""); + tokenSupply[id] += amount; + } + + function mintBatch( + address to, + uint256[] memory ids, + uint256[] memory amounts + ) external onlyOwner { + for (uint256 i = 0; i < ids.length; i++) { + require(tokenSupply[ids[i]] + amounts[i] <= maxSupply[ids[i]], "Exceeds max supply"); + tokenSupply[ids[i]] += amounts[i]; + } + + _mintBatch(to, ids, amounts, ""); + } + + function burn( + address from, + uint256 id, + uint256 amount + ) external { + require(from == msg.sender || isApprovedForAll(from, msg.sender), "Not authorized"); + _burn(from, id, amount); + tokenSupply[id] -= amount; + } +} +``` + +## Metadata Standards + +### Off-Chain Metadata (IPFS) +```json +{ + "name": "NFT #1", + "description": "Description of the NFT", + "image": "ipfs://QmImageHash", + "attributes": [ + { + "trait_type": "Background", + "value": "Blue" + }, + { + "trait_type": "Rarity", + "value": "Legendary" + }, + { + "trait_type": "Power", + "value": 95, + "display_type": "number", + "max_value": 100 + } + ] +} +``` + +### On-Chain Metadata +```solidity +contract OnChainNFT is ERC721 { + struct Traits { + uint8 background; + uint8 body; + uint8 head; + uint8 rarity; + } + + mapping(uint256 => Traits) public tokenTraits; + + function tokenURI(uint256 tokenId) public view override returns (string memory) { + Traits memory traits = tokenTraits[tokenId]; + + string memory json = Base64.encode( + bytes( + string( + abi.encodePacked( + '{"name": "NFT #', Strings.toString(tokenId), '",', + '"description": "On-chain NFT",', + '"image": "data:image/svg+xml;base64,', generateSVG(traits), '",', + '"attributes": [', + '{"trait_type": "Background", "value": "', Strings.toString(traits.background), '"},', + '{"trait_type": "Rarity", "value": "', getRarityName(traits.rarity), '"}', + ']}' + ) + ) + ) + ); + + return string(abi.encodePacked("data:application/json;base64,", json)); + } + + function generateSVG(Traits memory traits) internal pure returns (string memory) { + // Generate SVG based on traits + return "..."; + } +} +``` + +## Royalties (EIP-2981) + +```solidity +import "@openzeppelin/contracts/interfaces/IERC2981.sol"; + +contract NFTWithRoyalties is ERC721, IERC2981 { + address public royaltyRecipient; + uint96 public royaltyFee = 500; // 5% + + constructor() ERC721("Royalty NFT", "RNFT") { + royaltyRecipient = msg.sender; + } + + function royaltyInfo(uint256 tokenId, uint256 salePrice) + external + view + override + returns (address receiver, uint256 royaltyAmount) + { + return (royaltyRecipient, (salePrice * royaltyFee) / 10000); + } + + function setRoyalty(address recipient, uint96 fee) external onlyOwner { + require(fee <= 1000, "Royalty fee too high"); // Max 10% + royaltyRecipient = recipient; + royaltyFee = fee; + } + + function supportsInterface(bytes4 interfaceId) + public + view + override(ERC721, IERC165) + returns (bool) + { + return interfaceId == type(IERC2981).interfaceId || + super.supportsInterface(interfaceId); + } +} +``` + +## Soulbound Tokens (Non-Transferable) + +```solidity +contract SoulboundToken is ERC721 { + constructor() ERC721("Soulbound", "SBT") {} + + function _beforeTokenTransfer( + address from, + address to, + uint256 tokenId, + uint256 batchSize + ) internal virtual override { + require(from == address(0) || to == address(0), "Token is soulbound"); + super._beforeTokenTransfer(from, to, tokenId, batchSize); + } + + function mint(address to) external { + uint256 tokenId = totalSupply() + 1; + _safeMint(to, tokenId); + } + + // Burn is allowed (user can destroy their SBT) + function burn(uint256 tokenId) external { + require(ownerOf(tokenId) == msg.sender, "Not token owner"); + _burn(tokenId); + } +} +``` + +## Dynamic NFTs + +```solidity +contract DynamicNFT is ERC721 { + struct TokenState { + uint256 level; + uint256 experience; + uint256 lastUpdated; + } + + mapping(uint256 => TokenState) public tokenStates; + + function gainExperience(uint256 tokenId, uint256 exp) external { + require(ownerOf(tokenId) == msg.sender, "Not token owner"); + + TokenState storage state = tokenStates[tokenId]; + state.experience += exp; + + // Level up logic + if (state.experience >= state.level * 100) { + state.level++; + } + + state.lastUpdated = block.timestamp; + } + + function tokenURI(uint256 tokenId) public view override returns (string memory) { + TokenState memory state = tokenStates[tokenId]; + + // Generate metadata based on current state + return generateMetadata(tokenId, state); + } + + function generateMetadata(uint256 tokenId, TokenState memory state) + internal + pure + returns (string memory) + { + // Dynamic metadata generation + return ""; + } +} +``` + +## Gas-Optimized Minting (ERC721A) + +```solidity +import "erc721a/contracts/ERC721A.sol"; + +contract OptimizedNFT is ERC721A { + uint256 public constant MAX_SUPPLY = 10000; + uint256 public constant MINT_PRICE = 0.05 ether; + + constructor() ERC721A("Optimized NFT", "ONFT") {} + + function mint(uint256 quantity) external payable { + require(_totalMinted() + quantity <= MAX_SUPPLY, "Exceeds max supply"); + require(msg.value >= MINT_PRICE * quantity, "Insufficient payment"); + + _mint(msg.sender, quantity); + } + + function _baseURI() internal pure override returns (string memory) { + return "ipfs://QmBaseHash/"; + } +} +``` + +## Resources + +- **references/erc721.md**: ERC-721 specification details +- **references/erc1155.md**: ERC-1155 multi-token standard +- **references/metadata-standards.md**: Metadata best practices +- **references/enumeration.md**: Token enumeration patterns +- **assets/erc721-contract.sol**: Production ERC-721 template +- **assets/erc1155-contract.sol**: Production ERC-1155 template +- **assets/metadata-schema.json**: Standard metadata format +- **assets/metadata-uploader.py**: IPFS upload utility + +## Best Practices + +1. **Use OpenZeppelin**: Battle-tested implementations +2. **Pin Metadata**: Use IPFS with pinning service +3. **Implement Royalties**: EIP-2981 for marketplace compatibility +4. **Gas Optimization**: Use ERC721A for batch minting +5. **Reveal Mechanism**: Placeholder → reveal pattern +6. **Enumeration**: Support walletOfOwner for marketplaces +7. **Whitelist**: Merkle trees for efficient whitelisting + +## Marketplace Integration + +- OpenSea: ERC-721/1155, metadata standards +- LooksRare: Royalty enforcement +- Rarible: Protocol fees, lazy minting +- Blur: Gas-optimized trading diff --git a/skills/solidity-security/SKILL.md b/skills/solidity-security/SKILL.md new file mode 100644 index 0000000..cbd453e --- /dev/null +++ b/skills/solidity-security/SKILL.md @@ -0,0 +1,507 @@ +--- +name: solidity-security +description: Master smart contract security best practices to prevent common vulnerabilities and implement secure Solidity patterns. Use when writing smart contracts, auditing existing contracts, or implementing security measures for blockchain applications. +--- + +# Solidity Security + +Master smart contract security best practices, vulnerability prevention, and secure Solidity development patterns. + +## When to Use This Skill + +- Writing secure smart contracts +- Auditing existing contracts for vulnerabilities +- Implementing secure DeFi protocols +- Preventing reentrancy, overflow, and access control issues +- Optimizing gas usage while maintaining security +- Preparing contracts for professional audits +- Understanding common attack vectors + +## Critical Vulnerabilities + +### 1. Reentrancy +Attacker calls back into your contract before state is updated. + +**Vulnerable Code:** +```solidity +// VULNERABLE TO REENTRANCY +contract VulnerableBank { + mapping(address => uint256) public balances; + + function withdraw() public { + uint256 amount = balances[msg.sender]; + + // DANGER: External call before state update + (bool success, ) = msg.sender.call{value: amount}(""); + require(success); + + balances[msg.sender] = 0; // Too late! + } +} +``` + +**Secure Pattern (Checks-Effects-Interactions):** +```solidity +contract SecureBank { + mapping(address => uint256) public balances; + + function withdraw() public { + uint256 amount = balances[msg.sender]; + require(amount > 0, "Insufficient balance"); + + // EFFECTS: Update state BEFORE external call + balances[msg.sender] = 0; + + // INTERACTIONS: External call last + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + } +} +``` + +**Alternative: ReentrancyGuard** +```solidity +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +contract SecureBank is ReentrancyGuard { + mapping(address => uint256) public balances; + + function withdraw() public nonReentrant { + uint256 amount = balances[msg.sender]; + require(amount > 0, "Insufficient balance"); + + balances[msg.sender] = 0; + + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + } +} +``` + +### 2. Integer Overflow/Underflow + +**Vulnerable Code (Solidity < 0.8.0):** +```solidity +// VULNERABLE +contract VulnerableToken { + mapping(address => uint256) public balances; + + function transfer(address to, uint256 amount) public { + // No overflow check - can wrap around + balances[msg.sender] -= amount; // Can underflow! + balances[to] += amount; // Can overflow! + } +} +``` + +**Secure Pattern (Solidity >= 0.8.0):** +```solidity +// Solidity 0.8+ has built-in overflow/underflow checks +contract SecureToken { + mapping(address => uint256) public balances; + + function transfer(address to, uint256 amount) public { + // Automatically reverts on overflow/underflow + balances[msg.sender] -= amount; + balances[to] += amount; + } +} +``` + +**For Solidity < 0.8.0, use SafeMath:** +```solidity +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +contract SecureToken { + using SafeMath for uint256; + mapping(address => uint256) public balances; + + function transfer(address to, uint256 amount) public { + balances[msg.sender] = balances[msg.sender].sub(amount); + balances[to] = balances[to].add(amount); + } +} +``` + +### 3. Access Control + +**Vulnerable Code:** +```solidity +// VULNERABLE: Anyone can call critical functions +contract VulnerableContract { + address public owner; + + function withdraw(uint256 amount) public { + // No access control! + payable(msg.sender).transfer(amount); + } +} +``` + +**Secure Pattern:** +```solidity +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract SecureContract is Ownable { + function withdraw(uint256 amount) public onlyOwner { + payable(owner()).transfer(amount); + } +} + +// Or implement custom role-based access +contract RoleBasedContract { + mapping(address => bool) public admins; + + modifier onlyAdmin() { + require(admins[msg.sender], "Not an admin"); + _; + } + + function criticalFunction() public onlyAdmin { + // Protected function + } +} +``` + +### 4. Front-Running + +**Vulnerable:** +```solidity +// VULNERABLE TO FRONT-RUNNING +contract VulnerableDEX { + function swap(uint256 amount, uint256 minOutput) public { + // Attacker sees this in mempool and front-runs + uint256 output = calculateOutput(amount); + require(output >= minOutput, "Slippage too high"); + // Perform swap + } +} +``` + +**Mitigation:** +```solidity +contract SecureDEX { + mapping(bytes32 => bool) public usedCommitments; + + // Step 1: Commit to trade + function commitTrade(bytes32 commitment) public { + usedCommitments[commitment] = true; + } + + // Step 2: Reveal trade (next block) + function revealTrade( + uint256 amount, + uint256 minOutput, + bytes32 secret + ) public { + bytes32 commitment = keccak256(abi.encodePacked( + msg.sender, amount, minOutput, secret + )); + require(usedCommitments[commitment], "Invalid commitment"); + // Perform swap + } +} +``` + +## Security Best Practices + +### Checks-Effects-Interactions Pattern +```solidity +contract SecurePattern { + mapping(address => uint256) public balances; + + function withdraw(uint256 amount) public { + // 1. CHECKS: Validate conditions + require(amount <= balances[msg.sender], "Insufficient balance"); + require(amount > 0, "Amount must be positive"); + + // 2. EFFECTS: Update state + balances[msg.sender] -= amount; + + // 3. INTERACTIONS: External calls last + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + } +} +``` + +### Pull Over Push Pattern +```solidity +// Prefer this (pull) +contract SecurePayment { + mapping(address => uint256) public pendingWithdrawals; + + function recordPayment(address recipient, uint256 amount) internal { + pendingWithdrawals[recipient] += amount; + } + + function withdraw() public { + uint256 amount = pendingWithdrawals[msg.sender]; + require(amount > 0, "Nothing to withdraw"); + + pendingWithdrawals[msg.sender] = 0; + payable(msg.sender).transfer(amount); + } +} + +// Over this (push) +contract RiskyPayment { + function distributePayments(address[] memory recipients, uint256[] memory amounts) public { + for (uint i = 0; i < recipients.length; i++) { + // If any transfer fails, entire batch fails + payable(recipients[i]).transfer(amounts[i]); + } + } +} +``` + +### Input Validation +```solidity +contract SecureContract { + function transfer(address to, uint256 amount) public { + // Validate inputs + require(to != address(0), "Invalid recipient"); + require(to != address(this), "Cannot send to contract"); + require(amount > 0, "Amount must be positive"); + require(amount <= balances[msg.sender], "Insufficient balance"); + + // Proceed with transfer + balances[msg.sender] -= amount; + balances[to] += amount; + } +} +``` + +### Emergency Stop (Circuit Breaker) +```solidity +import "@openzeppelin/contracts/security/Pausable.sol"; + +contract EmergencyStop is Pausable, Ownable { + function criticalFunction() public whenNotPaused { + // Function logic + } + + function emergencyStop() public onlyOwner { + _pause(); + } + + function resume() public onlyOwner { + _unpause(); + } +} +``` + +## Gas Optimization + +### Use `uint256` Instead of Smaller Types +```solidity +// More gas efficient +contract GasEfficient { + uint256 public value; // Optimal + + function set(uint256 _value) public { + value = _value; + } +} + +// Less efficient +contract GasInefficient { + uint8 public value; // Still uses 256-bit slot + + function set(uint8 _value) public { + value = _value; // Extra gas for type conversion + } +} +``` + +### Pack Storage Variables +```solidity +// Gas efficient (3 variables in 1 slot) +contract PackedStorage { + uint128 public a; // Slot 0 + uint64 public b; // Slot 0 + uint64 public c; // Slot 0 + uint256 public d; // Slot 1 +} + +// Gas inefficient (each variable in separate slot) +contract UnpackedStorage { + uint256 public a; // Slot 0 + uint256 public b; // Slot 1 + uint256 public c; // Slot 2 + uint256 public d; // Slot 3 +} +``` + +### Use `calldata` Instead of `memory` for Function Arguments +```solidity +contract GasOptimized { + // More gas efficient + function processData(uint256[] calldata data) public pure returns (uint256) { + return data[0]; + } + + // Less efficient + function processDataMemory(uint256[] memory data) public pure returns (uint256) { + return data[0]; + } +} +``` + +### Use Events for Data Storage (When Appropriate) +```solidity +contract EventStorage { + // Emitting events is cheaper than storage + event DataStored(address indexed user, uint256 indexed id, bytes data); + + function storeData(uint256 id, bytes calldata data) public { + emit DataStored(msg.sender, id, data); + // Don't store in contract storage unless needed + } +} +``` + +## Common Vulnerabilities Checklist + +```solidity +// Security Checklist Contract +contract SecurityChecklist { + /** + * [ ] Reentrancy protection (ReentrancyGuard or CEI pattern) + * [ ] Integer overflow/underflow (Solidity 0.8+ or SafeMath) + * [ ] Access control (Ownable, roles, modifiers) + * [ ] Input validation (require statements) + * [ ] Front-running mitigation (commit-reveal if applicable) + * [ ] Gas optimization (packed storage, calldata) + * [ ] Emergency stop mechanism (Pausable) + * [ ] Pull over push pattern for payments + * [ ] No delegatecall to untrusted contracts + * [ ] No tx.origin for authentication (use msg.sender) + * [ ] Proper event emission + * [ ] External calls at end of function + * [ ] Check return values of external calls + * [ ] No hardcoded addresses + * [ ] Upgrade mechanism (if proxy pattern) + */ +} +``` + +## Testing for Security + +```javascript +// Hardhat test example +const { expect } = require("chai"); +const { ethers } = require("hardhat"); + +describe("Security Tests", function () { + it("Should prevent reentrancy attack", async function () { + const [attacker] = await ethers.getSigners(); + + const VictimBank = await ethers.getContractFactory("SecureBank"); + const bank = await VictimBank.deploy(); + + const Attacker = await ethers.getContractFactory("ReentrancyAttacker"); + const attackerContract = await Attacker.deploy(bank.address); + + // Deposit funds + await bank.deposit({value: ethers.utils.parseEther("10")}); + + // Attempt reentrancy attack + await expect( + attackerContract.attack({value: ethers.utils.parseEther("1")}) + ).to.be.revertedWith("ReentrancyGuard: reentrant call"); + }); + + it("Should prevent integer overflow", async function () { + const Token = await ethers.getContractFactory("SecureToken"); + const token = await Token.deploy(); + + // Attempt overflow + await expect( + token.transfer(attacker.address, ethers.constants.MaxUint256) + ).to.be.reverted; + }); + + it("Should enforce access control", async function () { + const [owner, attacker] = await ethers.getSigners(); + + const Contract = await ethers.getContractFactory("SecureContract"); + const contract = await Contract.deploy(); + + // Attempt unauthorized withdrawal + await expect( + contract.connect(attacker).withdraw(100) + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); +}); +``` + +## Audit Preparation + +```solidity +contract WellDocumentedContract { + /** + * @title Well Documented Contract + * @dev Example of proper documentation for audits + * @notice This contract handles user deposits and withdrawals + */ + + /// @notice Mapping of user balances + mapping(address => uint256) public balances; + + /** + * @dev Deposits ETH into the contract + * @notice Anyone can deposit funds + */ + function deposit() public payable { + require(msg.value > 0, "Must send ETH"); + balances[msg.sender] += msg.value; + } + + /** + * @dev Withdraws user's balance + * @notice Follows CEI pattern to prevent reentrancy + * @param amount Amount to withdraw in wei + */ + function withdraw(uint256 amount) public { + // CHECKS + require(amount <= balances[msg.sender], "Insufficient balance"); + + // EFFECTS + balances[msg.sender] -= amount; + + // INTERACTIONS + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + } +} +``` + +## Resources + +- **references/reentrancy.md**: Comprehensive reentrancy prevention +- **references/access-control.md**: Role-based access patterns +- **references/overflow-underflow.md**: SafeMath and integer safety +- **references/gas-optimization.md**: Gas saving techniques +- **references/vulnerability-patterns.md**: Common vulnerability catalog +- **assets/solidity-contracts-templates.sol**: Secure contract templates +- **assets/security-checklist.md**: Pre-audit checklist +- **scripts/analyze-contract.sh**: Static analysis tools + +## Tools for Security Analysis + +- **Slither**: Static analysis tool +- **Mythril**: Security analysis tool +- **Echidna**: Fuzzing tool +- **Manticore**: Symbolic execution +- **Securify**: Automated security scanner + +## Common Pitfalls + +1. **Using `tx.origin` for Authentication**: Use `msg.sender` instead +2. **Unchecked External Calls**: Always check return values +3. **Delegatecall to Untrusted Contracts**: Can hijack your contract +4. **Floating Pragma**: Pin to specific Solidity version +5. **Missing Events**: Emit events for state changes +6. **Excessive Gas in Loops**: Can hit block gas limit +7. **No Upgrade Path**: Consider proxy patterns if upgrades needed diff --git a/skills/web3-testing/SKILL.md b/skills/web3-testing/SKILL.md new file mode 100644 index 0000000..46674b7 --- /dev/null +++ b/skills/web3-testing/SKILL.md @@ -0,0 +1,399 @@ +--- +name: web3-testing +description: Test smart contracts comprehensively using Hardhat and Foundry with unit tests, integration tests, and mainnet forking. Use when testing Solidity contracts, setting up blockchain test suites, or validating DeFi protocols. +--- + +# Web3 Smart Contract Testing + +Master comprehensive testing strategies for smart contracts using Hardhat, Foundry, and advanced testing patterns. + +## When to Use This Skill + +- Writing unit tests for smart contracts +- Setting up integration test suites +- Performing gas optimization testing +- Fuzzing for edge cases +- Forking mainnet for realistic testing +- Automating test coverage reporting +- Verifying contracts on Etherscan + +## Hardhat Testing Setup + +```javascript +// hardhat.config.js +require("@nomicfoundation/hardhat-toolbox"); +require("@nomiclabs/hardhat-etherscan"); +require("hardhat-gas-reporter"); +require("solidity-coverage"); + +module.exports = { + solidity: { + version: "0.8.19", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + }, + networks: { + hardhat: { + forking: { + url: process.env.MAINNET_RPC_URL, + blockNumber: 15000000 + } + }, + goerli: { + url: process.env.GOERLI_RPC_URL, + accounts: [process.env.PRIVATE_KEY] + } + }, + gasReporter: { + enabled: true, + currency: 'USD', + coinmarketcap: process.env.COINMARKETCAP_API_KEY + }, + etherscan: { + apiKey: process.env.ETHERSCAN_API_KEY + } +}; +``` + +## Unit Testing Patterns + +```javascript +const { expect } = require("chai"); +const { ethers } = require("hardhat"); +const { loadFixture, time } = require("@nomicfoundation/hardhat-network-helpers"); + +describe("Token Contract", function () { + // Fixture for test setup + async function deployTokenFixture() { + const [owner, addr1, addr2] = await ethers.getSigners(); + + const Token = await ethers.getContractFactory("Token"); + const token = await Token.deploy(); + + return { token, owner, addr1, addr2 }; + } + + describe("Deployment", function () { + it("Should set the right owner", async function () { + const { token, owner } = await loadFixture(deployTokenFixture); + expect(await token.owner()).to.equal(owner.address); + }); + + it("Should assign total supply to owner", async function () { + const { token, owner } = await loadFixture(deployTokenFixture); + const ownerBalance = await token.balanceOf(owner.address); + expect(await token.totalSupply()).to.equal(ownerBalance); + }); + }); + + describe("Transactions", function () { + it("Should transfer tokens between accounts", async function () { + const { token, owner, addr1 } = await loadFixture(deployTokenFixture); + + await expect(token.transfer(addr1.address, 50)) + .to.changeTokenBalances(token, [owner, addr1], [-50, 50]); + }); + + it("Should fail if sender doesn't have enough tokens", async function () { + const { token, addr1 } = await loadFixture(deployTokenFixture); + const initialBalance = await token.balanceOf(addr1.address); + + await expect( + token.connect(addr1).transfer(owner.address, 1) + ).to.be.revertedWith("Insufficient balance"); + }); + + it("Should emit Transfer event", async function () { + const { token, owner, addr1 } = await loadFixture(deployTokenFixture); + + await expect(token.transfer(addr1.address, 50)) + .to.emit(token, "Transfer") + .withArgs(owner.address, addr1.address, 50); + }); + }); + + describe("Time-based tests", function () { + it("Should handle time-locked operations", async function () { + const { token } = await loadFixture(deployTokenFixture); + + // Increase time by 1 day + await time.increase(86400); + + // Test time-dependent functionality + }); + }); + + describe("Gas optimization", function () { + it("Should use gas efficiently", async function () { + const { token } = await loadFixture(deployTokenFixture); + + const tx = await token.transfer(addr1.address, 100); + const receipt = await tx.wait(); + + expect(receipt.gasUsed).to.be.lessThan(50000); + }); + }); +}); +``` + +## Foundry Testing (Forge) + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import "../src/Token.sol"; + +contract TokenTest is Test { + Token token; + address owner = address(1); + address user1 = address(2); + address user2 = address(3); + + function setUp() public { + vm.prank(owner); + token = new Token(); + } + + function testInitialSupply() public { + assertEq(token.totalSupply(), 1000000 * 10**18); + } + + function testTransfer() public { + vm.prank(owner); + token.transfer(user1, 100); + + assertEq(token.balanceOf(user1), 100); + assertEq(token.balanceOf(owner), token.totalSupply() - 100); + } + + function testFailTransferInsufficientBalance() public { + vm.prank(user1); + token.transfer(user2, 100); // Should fail + } + + function testCannotTransferToZeroAddress() public { + vm.prank(owner); + vm.expectRevert("Invalid recipient"); + token.transfer(address(0), 100); + } + + // Fuzzing test + function testFuzzTransfer(uint256 amount) public { + vm.assume(amount > 0 && amount <= token.totalSupply()); + + vm.prank(owner); + token.transfer(user1, amount); + + assertEq(token.balanceOf(user1), amount); + } + + // Test with cheatcodes + function testDealAndPrank() public { + // Give ETH to address + vm.deal(user1, 10 ether); + + // Impersonate address + vm.prank(user1); + + // Test functionality + assertEq(user1.balance, 10 ether); + } + + // Mainnet fork test + function testForkMainnet() public { + vm.createSelectFork("https://eth-mainnet.alchemyapi.io/v2/..."); + + // Interact with mainnet contracts + address dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + assertEq(IERC20(dai).symbol(), "DAI"); + } +} +``` + +## Advanced Testing Patterns + +### Snapshot and Revert +```javascript +describe("Complex State Changes", function () { + let snapshotId; + + beforeEach(async function () { + snapshotId = await network.provider.send("evm_snapshot"); + }); + + afterEach(async function () { + await network.provider.send("evm_revert", [snapshotId]); + }); + + it("Test 1", async function () { + // Make state changes + }); + + it("Test 2", async function () { + // State reverted, clean slate + }); +}); +``` + +### Mainnet Forking +```javascript +describe("Mainnet Fork Tests", function () { + let uniswapRouter, dai, usdc; + + before(async function () { + await network.provider.request({ + method: "hardhat_reset", + params: [{ + forking: { + jsonRpcUrl: process.env.MAINNET_RPC_URL, + blockNumber: 15000000 + } + }] + }); + + // Connect to existing mainnet contracts + uniswapRouter = await ethers.getContractAt( + "IUniswapV2Router", + "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" + ); + + dai = await ethers.getContractAt( + "IERC20", + "0x6B175474E89094C44Da98b954EedeAC495271d0F" + ); + }); + + it("Should swap on Uniswap", async function () { + // Test with real Uniswap contracts + }); +}); +``` + +### Impersonating Accounts +```javascript +it("Should impersonate whale account", async function () { + const whaleAddress = "0x..."; + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [whaleAddress] + }); + + const whale = await ethers.getSigner(whaleAddress); + + // Use whale's tokens + await dai.connect(whale).transfer(addr1.address, ethers.utils.parseEther("1000")); +}); +``` + +## Gas Optimization Testing + +```javascript +const { expect } = require("chai"); + +describe("Gas Optimization", function () { + it("Compare gas usage between implementations", async function () { + const Implementation1 = await ethers.getContractFactory("OptimizedContract"); + const Implementation2 = await ethers.getContractFactory("UnoptimizedContract"); + + const contract1 = await Implementation1.deploy(); + const contract2 = await Implementation2.deploy(); + + const tx1 = await contract1.doSomething(); + const receipt1 = await tx1.wait(); + + const tx2 = await contract2.doSomething(); + const receipt2 = await tx2.wait(); + + console.log("Optimized gas:", receipt1.gasUsed.toString()); + console.log("Unoptimized gas:", receipt2.gasUsed.toString()); + + expect(receipt1.gasUsed).to.be.lessThan(receipt2.gasUsed); + }); +}); +``` + +## Coverage Reporting + +```bash +# Generate coverage report +npx hardhat coverage + +# Output shows: +# File | % Stmts | % Branch | % Funcs | % Lines | +# -------------------|---------|----------|---------|---------| +# contracts/Token.sol | 100 | 90 | 100 | 95 | +``` + +## Contract Verification + +```javascript +// Verify on Etherscan +await hre.run("verify:verify", { + address: contractAddress, + constructorArguments: [arg1, arg2] +}); +``` + +```bash +# Or via CLI +npx hardhat verify --network mainnet CONTRACT_ADDRESS "Constructor arg1" "arg2" +``` + +## CI/CD Integration + +```yaml +# .github/workflows/test.yml +name: Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '16' + + - run: npm install + - run: npx hardhat compile + - run: npx hardhat test + - run: npx hardhat coverage + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 +``` + +## Resources + +- **references/hardhat-setup.md**: Hardhat configuration guide +- **references/foundry-setup.md**: Foundry testing framework +- **references/test-patterns.md**: Testing best practices +- **references/mainnet-forking.md**: Fork testing strategies +- **references/contract-verification.md**: Etherscan verification +- **assets/hardhat-config.js**: Complete Hardhat configuration +- **assets/test-suite.js**: Comprehensive test examples +- **assets/foundry.toml**: Foundry configuration +- **scripts/test-contract.sh**: Automated testing script + +## Best Practices + +1. **Test Coverage**: Aim for >90% coverage +2. **Edge Cases**: Test boundary conditions +3. **Gas Limits**: Verify functions don't hit block gas limit +4. **Reentrancy**: Test for reentrancy vulnerabilities +5. **Access Control**: Test unauthorized access attempts +6. **Events**: Verify event emissions +7. **Fixtures**: Use fixtures to avoid code duplication +8. **Mainnet Fork**: Test with real contracts +9. **Fuzzing**: Use property-based testing +10. **CI/CD**: Automate testing on every commit