Initial commit
This commit is contained in:
18
.claude-plugin/plugin.json
Normal file
18
.claude-plugin/plugin.json
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# blockchain-web3
|
||||
|
||||
Smart contract development with Solidity, DeFi protocol implementation, NFT platforms, and Web3 application architecture
|
||||
171
agents/blockchain-developer.md
Normal file
171
agents/blockchain-developer.md
Normal file
@@ -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"
|
||||
61
plugin.lock.json
Normal file
61
plugin.lock.json
Normal file
@@ -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": []
|
||||
}
|
||||
}
|
||||
454
skills/defi-protocol-templates/SKILL.md
Normal file
454
skills/defi-protocol-templates/SKILL.md
Normal file
@@ -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
|
||||
381
skills/nft-standards/SKILL.md
Normal file
381
skills/nft-standards/SKILL.md
Normal file
@@ -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
|
||||
507
skills/solidity-security/SKILL.md
Normal file
507
skills/solidity-security/SKILL.md
Normal file
@@ -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
|
||||
399
skills/web3-testing/SKILL.md
Normal file
399
skills/web3-testing/SKILL.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user