---
name: solidity-development
description: "สกิลสำหรับ การพัฒนา Solidity ใช้เมื่อต้องการแนวทาง ขั้นตอน Template หรือคำแนะนำเชิงปฏิบัติสำหรับงาน Blockchain, Web3, Smart Contract, DeFi, Wallet และระบบกระจายศูนย์ ในหัวข้อนี้"
allowed-tools: Read, Write, Edit, Bash
version: "1.0"
---

# การพัฒนา Solidity

> แนวทางการเขียน Solidity สำหรับ production-grade smart contracts

---

## Project Structure

```
contracts/
├── tokens/
│   ├── MyToken.sol
│   └── NFT.sol
├── governance/
│   └── DAO.sol
├── interfaces/
│   └── IMyToken.sol
├── libraries/
│   └── MathLib.sol
└── mocks/             ← ใช้เฉพาะ testing
    └── MockERC20.sol
test/
scripts/
    deploy.js
hardhat.config.js
```

---

## Naming Conventions

| Element | Convention | ตัวอย่าง |
|---------|-----------|---------|
| Contract | PascalCase | `TokenVault`, `StakingPool` |
| Function | camelCase | `depositTokens()`, `claimReward()` |
| Variable | camelCase | `totalSupply`, `lastUpdated` |
| Constant | UPPER_SNAKE | `MAX_SUPPLY`, `FEE_BPS` |
| Event | PascalCase | `Deposited`, `Withdrawn` |
| Error | PascalCase | `InsufficientBalance`, `Unauthorized` |
| Interface | I + PascalCase | `IToken`, `IVault` |

---

## Gas Optimization

### Storage vs Memory
```solidity
// ❌ อ่าน storage ซ้ำหลายครั้ง (แพง)
function sumBalances(address[] calldata users) external view returns (uint total) {
    for (uint i; i < users.length; i++) {
        total += balances[users[i]]; // storage read ทุก loop
    }
}

// ✅ Cache storage ใน memory
function sumBalances(address[] calldata users) external view returns (uint total) {
    mapping(address => uint) storage _balances = balances; // cache reference
    for (uint i; i < users.length; i++) {
        total += _balances[users[i]];
    }
}
```

### Packing Storage Slots
```solidity
// ❌ ใช้ 3 slots (แต่ละ uint256 = 1 slot)
uint256 a;
uint256 b;
uint256 c;

// ✅ Pack ให้อยู่ใน 1 slot (ถ้า type เล็กพอ)
uint128 a;  // 16 bytes
uint64  b;  // 8 bytes
uint64  c;  // 8 bytes  → รวม = 32 bytes = 1 slot
```

### Custom Errors (Solidity ≥ 0.8.4)
```solidity
// ❌ String error — แพงกว่า
require(balance >= amount, "Insufficient balance");

// ✅ Custom error — ถูกกว่า ~50% gas
error InsufficientBalance(uint available, uint required);
if (balance < amount) revert InsufficientBalance(balance, amount);
```

---

## Common Token Standards

### ERC-20 (Fungible Token)
```solidity
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor(uint256 initialSupply) ERC20("MyToken", "MTK") Ownable(msg.sender) {
        _mint(msg.sender, initialSupply * 10 ** decimals());
    }

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
    }
}
```

### ERC-721 (NFT)
```solidity
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT is ERC721, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    string private _baseTokenURI;

    constructor(string memory baseURI) ERC721("MyNFT", "MNFT") Ownable(msg.sender) {
        _baseTokenURI = baseURI;
    }

    function mint(address to) external onlyOwner returns (uint256) {
        _tokenIds.increment();
        uint256 tokenId = _tokenIds.current();
        _safeMint(to, tokenId);
        return tokenId;
    }

    function _baseURI() internal view override returns (string memory) {
        return _baseTokenURI;
    }
}
```

---

## Events — Log ทุก State Change สำคัญ

```solidity
event Deposited(address indexed user, uint256 amount, uint256 timestamp);
event Withdrawn(address indexed user, uint256 amount);
event OwnerUpdated(address indexed oldOwner, address indexed newOwner);

// emit ทันทีหลัง state change
function deposit(uint256 amount) external {
    balances[msg.sender] += amount;
    emit Deposited(msg.sender, amount, block.timestamp);
}
```

---

## Testing Standards (Hardhat + Chai)

```javascript
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("MyToken", () => {
    let token, owner, user;

    beforeEach(async () => {
        [owner, user] = await ethers.getSigners();
        const MyToken = await ethers.getContractFactory("MyToken");
        token = await MyToken.deploy(ethers.parseEther("1000000"));
    });

    it("should mint initial supply to owner", async () => {
        expect(await token.balanceOf(owner.address))
            .to.equal(ethers.parseEther("1000000"));
    });

    it("should revert if non-owner mints", async () => {
        await expect(token.connect(user).mint(user.address, 100))
            .to.be.revertedWithCustomError(token, "OwnableUnauthorizedAccount");
    });
});
```

---

## Deployment Checklist

| ขั้นตอน | คำอธิบาย |
|---------|---------|
| 1. Compile | `npx hardhat compile` — ไม่มี warnings |
| 2. Test | `npx hardhat test --coverage` — ≥ 95% |
| 3. Testnet | deploy + ทดสอบ ทุก function |
| 4. Verify | `npx hardhat verify` บน block explorer |
| 5. Audit | external audit ก่อน mainnet (DeFi ที่มีมูลค่าสูง) |
| 6. Deploy | ใช้ hardware wallet หรือ multisig |
| 7. Monitor | ตั้ง alert บน Tenderly หรือ OpenZeppelin Defender |
