solidity-coding

[AUTO-INVOKE] MUST be invoked BEFORE writing or modifying any Solidity contract (.sol files). Covers pragma version, naming conventions, project layout, OpenZeppelin library selection standards, oracle integration, and anti-patterns. Trigger: any task involving creating, editing, or reviewing .sol source files.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "solidity-coding" with this command: npx skills add 0xlayerghost/solidity-agent-kit/0xlayerghost-solidity-agent-kit-solidity-coding

Solidity Coding Standards

Language Rule

  • Always respond in the same language the user is using. If the user asks in Chinese, respond in Chinese. If in English, respond in English.

Coding Principles

  • Pragma: Use pragma solidity ^0.8.19; — keep consistent across all files in the project
  • Dependencies: OpenZeppelin Contracts 4.9.x, manage imports via remappings.txt
  • Error Handling: Prefer custom errors over require strings — saves gas and is more expressive
    • Define: error InsufficientBalance(uint256 available, uint256 required);
    • Use: if (balance < amount) revert InsufficientBalance(balance, amount);
  • Documentation: All public / external functions must have NatSpec (@notice, @param, @return)
  • Event Indexing: Only add indexed to address type parameters — add comment if indexing other types
  • Special Keywords: immutable / constant / unchecked / assembly must have inline comment explaining why

Event Design Patterns

Reason-Annotated Events(原因标注事件)

When an event represents a conditional/branching outcome (the same action can happen for different reasons), add a string reason parameter to make on-chain data self-documenting:

// GOOD — on-chain data is self-explanatory
event WithdrawalFailed(address indexed user, uint256 amount, string reason);
emit WithdrawalFailed(user, amount, "insufficient balance");
emit WithdrawalFailed(user, amount, "cooldown not expired");

event ProposalRejected(uint256 indexed proposalId, string reason);
emit ProposalRejected(id, "quorum not reached");
emit ProposalRejected(id, "voting period ended");

// BAD — must read contract source to understand why
event WithdrawalFailed(address indexed user, uint256 amount);

适用场景

  • 资金去向分叉:同一笔资金因不同条件流向不同地址
  • 操作被拒绝/降级:用户请求未完全满足,附带原因
  • 状态转换:同一个状态变更可能由不同触发条件引起
  • 管理员操作:记录 why(如 "security incident", "parameter tuning"

不适用场景

  • 事件只有一种触发路径(无分支)→ 不需要 reason
  • 高频事件(每笔 swap/transfer)→ string 消耗额外 gas,用 uint8 reasonCode 代替

Reason 实现方式选择

场景方式Gas 成本
原因种类少且固定(≤5种)string reason 字面量低(编译器优化短字符串)
原因种类多或动态uint8 reasonCode + 前端映射表最低
需要附带动态数据string reason 拼接较高,慎用
// 方式 A:string literal(推荐,可读性最好)
emit WithdrawalFailed(user, amount, "cooldown not expired");

// 方式 B:uint8 code(高频场景省 gas)
event SwapRejected(address indexed user, uint256 amount, uint8 reasonCode);
// 0 = slippage, 1 = insufficient liquidity, 2 = paused
emit SwapRejected(user, amount, 0);

General Event Design Rules

  • 每个改变状态的 external / public 函数必须 emit 至少一个事件
  • 事件参数应包含足够信息让前端/indexer 无需额外 RPC 调用就能重建完整上下文
  • NatSpec @param 必须为 reason 参数列出所有可能的值

Naming Conventions

ElementConventionExample
Contract / LibraryPascalCaseMyToken, StakingPool
InterfaceI + PascalCaseIMyToken, IStakingPool
State variable / FunctionlowerCamelCasetotalSupply, claimDividend
Constant / ImmutableUPPER_SNAKE_CASEMAX_SUPPLY, ROUTER_ADDRESS
EventPascalCase (past tense)TokenTransferred, PoolCreated
Custom ErrorPascalCaseInsufficientBalance, Unauthorized
Function parameterprefix _ for setterfunction setFee(uint256 _fee)
  • Forbidden: Pinyin names, single-letter variables (except i/j/k in loops), excessive abbreviations

Code Organization Rules

SituationRule
Cross-contract constantsPlace in src/common/Const.sol
Interface definitionsPlace in src/interfaces/I<Name>.sol, separate from implementation
Simple on-chain queriesUse Foundry cast CLI (call / send)
Complex multi-step operationsUse Foundry script (*.s.sol)
Import styleUse named imports: import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

Project Directory Structure

src/              — Contract source code
  interfaces/     — Interface definitions (I*.sol)
  common/         — Shared constants, types, errors (Const.sol, Types.sol)
test/             — Test files (*.t.sol)
script/           — Deployment & interaction scripts (*.s.sol)
config/           — Network config, parameters (*.json)
deployments/      — Deployment records (latest.env)
docs/             — Documentation, changelogs
lib/              — Dependencies (managed by Foundry)

Configuration Management

  • config/*.json — network RPC URLs, contract addresses, business parameters
  • deployments/latest.env — latest deployed contract addresses, must update after each deployment
  • foundry.toml — compiler version, optimizer settings, remappings
  • Important config changes must be documented in the PR description

OpenZeppelin Library Selection Standards

When writing Solidity contracts, prioritize using battle-tested OpenZeppelin libraries over custom implementations. Select the appropriate library based on the scenario:

Access Control

ScenarioLibraryImport Path
Single owner managementOwnable@openzeppelin/contracts/access/Ownable.sol
Owner transfer needs safetyOwnable2Step@openzeppelin/contracts/access/Ownable2Step.sol
Multi-role permission (admin/operator/minter)AccessControl@openzeppelin/contracts/access/AccessControl.sol
Need to enumerate role membersAccessControlEnumerable@openzeppelin/contracts/access/AccessControlEnumerable.sol
Governance with timelock delayTimelockController@openzeppelin/contracts/governance/TimelockController.sol

Rule: Single owner → Ownable2Step; 2+ roles → AccessControl; governance/DAO → TimelockController

Security Protection

ScenarioLibraryUsage
External call / token transferReentrancyGuardAdd nonReentrant modifier
Emergency pause neededPausableAdd whenNotPaused to user-facing functions; keep admin functions unpaused
ERC20 token interactionSafeERC20Use safeTransfer / safeTransferFrom / safeApprove instead of raw calls

Rule: Any contract that transfers tokens or ETH MUST use ReentrancyGuard + SafeERC20

Token Standards

ScenarioLibraryNotes
Fungible tokenERC20Base standard
Token with burn mechanismERC20BurnableAdds burn() and burnFrom()
Token with max supply capERC20CappedEnforces totalSupply <= cap
Gasless approval (EIP-2612)ERC20PermitSaves users approve tx gas
Governance voting tokenERC20VotesSnapshot-based voting power
NFTERC721Base NFT standard
NFT with enumerationERC721EnumerableSupports tokenOfOwnerByIndex queries
Multi-token (FT + NFT mixed)ERC1155Game items, batch operations

Utility Libraries

ScenarioLibraryUsage
Whitelist / airdrop verificationMerkleProofGas-efficient Merkle tree verification
Signature verificationECDSA + EIP712Off-chain sign + on-chain verify
Auto-increment IDsCountersToken ID, order ID generation
Batch function callsMulticallMultiple operations in one tx
Address set / uint setEnumerableSetIterable sets with O(1) add/remove/contains
Revenue sharingPaymentSplitterSplit ETH/token payments by shares
Standardized yield vaultERC4626DeFi vault standard

Contract Upgrade

ScenarioLibraryNotes
Upgradeable contract (gas efficient)UUPSUpgradeableUpgrade logic in implementation contract
Upgradeable contract (admin separated)TransparentUpgradeableProxyUpgrade logic in proxy, higher gas
Initializer (replace constructor)InitializableUse initializer modifier instead of constructor

Rule: New projects prefer UUPSUpgradeable; always use Initializable for upgradeable contracts

Oracle and Off-Chain Services

ScenarioLibraryNotes
Token price dataAggregatorV3InterfaceOnly for tokens with supported oracle data feeds
Verifiable randomness (lottery/NFT)VRFConsumerBaseV2On-chain provably fair random numbers
Automated execution (cron jobs)AutomationCompatibleReplace centralized keepers
Cross-chain messagingCCIPCross-chain token/message transfer

Library Selection Decision Flow

Does contract handle user funds/tokens?
├── YES → Add ReentrancyGuard + SafeERC20
│         Does it need emergency stop?
│         ├── YES → Add Pausable
│         └── NO  → Skip
└── NO  → Skip

How many admin roles needed?
├── 1 role  → Ownable2Step
├── 2+ roles → AccessControl
└── DAO/governance → TimelockController

Does contract need price data?
├── Token has oracle feed → AggregatorV3Interface
├── No oracle feed → Custom TWAP with min-liquidity check
└── No price needed → Skip

Will contract need upgrades?
├── YES → UUPSUpgradeable + Initializable
└── NO  → Standard deployment (immutable)

Anti-Patterns (Do NOT)

  • Do NOT write custom transfer wrappers — use SafeERC20
  • Do NOT write custom access control modifiers — use Ownable / AccessControl
  • Do NOT write custom pause logic — use Pausable
  • Do NOT use SafeMath on Solidity >= 0.8.0 — overflow checks are built-in
  • Do NOT use require(token.transfer(...)) — use token.safeTransfer(...) via SafeERC20
  • Do NOT use tx.origin for auth — use msg.sender with Ownable / AccessControl

MCP-Assisted Contract Generation (if available)

When OpenZeppelinContracts MCP is configured, prefer using it to generate base contracts instead of writing from scratch:

Contract TypeMCP ToolWhen to Use
Fungible tokensolidity-erc20Any new ERC20 token contract
NFTsolidity-erc721Any new NFT contract
Multi-tokensolidity-erc1155Game items, batch operations
Stablecoinsolidity-stablecoinStablecoin with ERC20 compliance
Real-world assetssolidity-rwaAsset tokenization
Smart accountsolidity-accountERC-4337 account abstraction
Governancesolidity-governorDAO voting and proposals
Customsolidity-customNon-standard contracts with OZ patterns

Workflow: MCP generates base → apply this skill's naming/structure rules → customize business logic → apply /solidity-security rules

Why MCP over manual: MCP output is validated against the same rule-set as OZ Contracts Wizard — imports, modifiers, security checks are guaranteed correct. Manual coding risks missing imports or using wrong OZ versions.

When NOT to use MCP: Heavily custom contracts with non-standard patterns, contracts that don't fit any OZ template, or when you need fine-grained control from line 1.

Graceful degradation: If MCP is not configured, fall back to the Library Selection Standards above and write contracts manually following all rules in this skill.

Foundry Quick Reference

OperationCommand
Create new projectforge init <project-name>
Install dependencyforge install openzeppelin-contracts
Build contractsforge build
Format codeforge fmt
Update remappingsforge remappings

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

solidity-testing

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

solidity-debug

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

solidity-deploy

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

git-workflow

No summary provided by upstream source.

Repository SourceNeeds Review