From 50ebf1605a78cb775a2488820a53992e0a538241 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Sun, 5 Apr 2026 16:12:42 -0700 Subject: [PATCH] feat: add blockchain security skill bundle --- AGENTS.md | 4 +- README.md | 6 +- README.zh-CN.md | 2 +- WORKING-CONTEXT.md | 1 + docs/zh-CN/AGENTS.md | 4 +- docs/zh-CN/README.md | 6 +- manifests/install-modules.json | 4 + skills/defi-amm-security/SKILL.md | 160 +++++++++++++++++++++ skills/evm-token-decimals/SKILL.md | 130 +++++++++++++++++ skills/llm-trading-agent-security/SKILL.md | 146 +++++++++++++++++++ skills/nodejs-keccak256/SKILL.md | 102 +++++++++++++ 11 files changed, 554 insertions(+), 11 deletions(-) create mode 100644 skills/defi-amm-security/SKILL.md create mode 100644 skills/evm-token-decimals/SKILL.md create mode 100644 skills/llm-trading-agent-security/SKILL.md create mode 100644 skills/nodejs-keccak256/SKILL.md diff --git a/AGENTS.md b/AGENTS.md index 5ee322f5..33e6efd0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 39 specialized agents, 164 skills, 72 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 39 specialized agents, 168 skills, 72 commands, and automated hook workflows for software development. **Version:** 1.10.0 @@ -146,7 +146,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 39 specialized subagents -skills/ — 164 workflow skills and domain knowledge +skills/ — 168 workflow skills and domain knowledge commands/ — 72 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index dcf48633..9d3c56cf 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,7 @@ For manual install instructions see the README in the `rules/` folder. When copy /plugin list ecc@ecc ``` -**That's it!** You now have access to 39 agents, 164 skills, and 72 legacy command shims. +**That's it!** You now have access to 39 agents, 168 skills, and 72 legacy command shims. ### Multi-model commands require additional setup @@ -1154,7 +1154,7 @@ The configuration is automatically detected from `.opencode/opencode.json`. |---------|-------------|----------|--------| | Agents | PASS: 39 agents | PASS: 12 agents | **Claude Code leads** | | Commands | PASS: 72 commands | PASS: 31 commands | **Claude Code leads** | -| Skills | PASS: 164 skills | PASS: 37 skills | **Claude Code leads** | +| Skills | PASS: 168 skills | PASS: 37 skills | **Claude Code leads** | | Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** | | Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** | | MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** | @@ -1263,7 +1263,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e |---------|------------|------------|-----------|----------| | **Agents** | 39 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | | **Commands** | 72 | Shared | Instruction-based | 31 | -| **Skills** | 164 | Shared | 10 (native format) | 37 | +| **Skills** | 168 | Shared | 10 (native format) | 37 | | **Hook Events** | 8 types | 15 types | None yet | 11 types | | **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | | **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | diff --git a/README.zh-CN.md b/README.zh-CN.md index ef3c978a..b342e42d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -106,7 +106,7 @@ cp -r everything-claude-code/rules/perl ~/.claude/rules/ /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 39 个代理、164 个技能和 72 个命令。 +**完成!** 你现在可以使用 39 个代理、168 个技能和 72 个命令。 ### multi-* 命令需要额外配置 diff --git a/WORKING-CONTEXT.md b/WORKING-CONTEXT.md index d65c5a3f..33505bfd 100644 --- a/WORKING-CONTEXT.md +++ b/WORKING-CONTEXT.md @@ -92,6 +92,7 @@ Keep this file detailed for only the current sprint, blockers, and next actions. - 2026-04-05: Fixed the `main` npm CI break after the latest direct ports. `package-lock.json` had drifted behind `package.json` on the `globals` devDependency (`^17.1.0` vs `^17.4.0`), which caused all npm-based GitHub Actions jobs to fail at `npm ci`. Refreshed the lockfile only, verified `npm ci --ignore-scripts`, and kept the mixed-lock workspace otherwise untouched. - 2026-04-05: Direct-ported the useful discoverability part of `#1221` without duplicating a second healthcare compliance system. Added `skills/hipaa-compliance/SKILL.md` as a thin HIPAA-specific entrypoint that points into the canonical `healthcare-phi-compliance` / `healthcare-reviewer` lane, and wired both healthcare privacy skills into the `security` install module for selective installs. +- 2026-04-05: Direct-ported the audited blockchain/web3 security lane from `#1222` into `main` as four self-contained skills: `defi-amm-security`, `evm-token-decimals`, `llm-trading-agent-security`, and `nodejs-keccak256`. These are now part of the `security` install module instead of living as an unmerged fork PR. - 2026-04-02: `ECC-Tools/main` shipped `9566637` (`fix: prefer commit lookup over git ref resolution`). The PR-analysis fire is now fixed in the app repo by preferring explicit commit resolution before `git.getRef`, with regression coverage for pull refs and plain branch refs. Mirrored public tracking issue `#1184` in this repo was closed as resolved upstream. - 2026-04-02: Direct-ported the clean native-support core of `#1043` into `main`: `agents/csharp-reviewer.md`, `skills/dotnet-patterns/SKILL.md`, and `skills/csharp-testing/SKILL.md`. This fills the gap between existing C# rule/docs mentions and actual shipped C# review/testing guidance. - 2026-04-02: Direct-ported the clean native-support core of `#1055` into `main`: `agents/dart-build-resolver.md`, `commands/flutter-build.md`, `commands/flutter-review.md`, `commands/flutter-test.md`, `rules/dart/*`, and `skills/dart-flutter-patterns/SKILL.md`. The skill paths were wired into the current `framework-language` module instead of replaying the older PR's separate `flutter-dart` module layout. diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 89fcd7bf..33484893 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 39 个专业代理、164 项技能、72 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 39 个专业代理、168 项技能、72 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 1.10.0 @@ -147,7 +147,7 @@ ``` agents/ — 39 个专业子代理 -skills/ — 164 个工作流技能和领域知识 +skills/ — 168 个工作流技能和领域知识 commands/ — 72 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 4babf9e0..2e80bb41 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -209,7 +209,7 @@ npx ecc-install typescript /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 39 个智能体、164 项技能和 72 个命令了。 +**搞定!** 你现在可以使用 39 个智能体、168 项技能和 72 个命令了。 *** @@ -1096,7 +1096,7 @@ opencode |---------|-------------|----------|--------| | 智能体 | PASS: 39 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 72 个 | PASS: 31 个 | **Claude Code 领先** | -| 技能 | PASS: 164 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 168 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1208,7 +1208,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|------------|------------|-----------|----------| | **智能体** | 39 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 72 | 共享 | 基于指令 | 31 | -| **技能** | 164 | 共享 | 10 (原生格式) | 37 | +| **技能** | 168 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/manifests/install-modules.json b/manifests/install-modules.json index dd450c3e..d3c39f01 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -234,14 +234,18 @@ "kind": "skills", "description": "Security review and security-focused framework guidance.", "paths": [ + "skills/defi-amm-security", "skills/django-security", "skills/healthcare-phi-compliance", "skills/hipaa-compliance", "skills/laravel-security", + "skills/llm-trading-agent-security", + "skills/nodejs-keccak256", "skills/perl-security", "skills/security-review", "skills/security-scan", "skills/springboot-security", + "skills/evm-token-decimals", "the-security-guide.md" ], "targets": [ diff --git a/skills/defi-amm-security/SKILL.md b/skills/defi-amm-security/SKILL.md new file mode 100644 index 00000000..faf8aca7 --- /dev/null +++ b/skills/defi-amm-security/SKILL.md @@ -0,0 +1,160 @@ +--- +name: defi-amm-security +description: Security checklist for Solidity AMM contracts, liquidity pools, and swap flows. Covers reentrancy, CEI ordering, donation or inflation attacks, oracle manipulation, slippage, admin controls, and integer math. +origin: ECC direct-port adaptation +version: "1.0.0" +--- + +# DeFi AMM Security + +Critical vulnerability patterns and hardened implementations for Solidity AMM contracts, LP vaults, and swap functions. + +## When to Use + +- Writing or auditing a Solidity AMM or liquidity-pool contract +- Implementing swap, deposit, withdraw, mint, or burn flows that hold token balances +- Reviewing any contract that uses `token.balanceOf(address(this))` in share or reserve math +- Adding fee setters, pausers, oracle updates, or other admin functions to a DeFi protocol + +## How It Works + +Use this as a checklist-plus-pattern library. Review every user entrypoint against the categories below and prefer the hardened examples over hand-rolled variants. + +## Examples + +### Reentrancy: enforce CEI order + +Vulnerable: + +```solidity +function withdraw(uint256 amount) external { + require(balances[msg.sender] >= amount); + token.transfer(msg.sender, amount); + balances[msg.sender] -= amount; +} +``` + +Safe: + +```solidity +import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +using SafeERC20 for IERC20; + +function withdraw(uint256 amount) external nonReentrant { + require(balances[msg.sender] >= amount, "Insufficient"); + balances[msg.sender] -= amount; + token.safeTransfer(msg.sender, amount); +} +``` + +Do not write your own guard when a hardened library exists. + +### Donation or inflation attacks + +Using `token.balanceOf(address(this))` directly for share math lets attackers manipulate the denominator by sending tokens to the contract outside the intended path. + +```solidity +// Vulnerable +function deposit(uint256 assets) external returns (uint256 shares) { + shares = (assets * totalShares) / token.balanceOf(address(this)); +} +``` + +```solidity +// Safe +uint256 private _totalAssets; + +function deposit(uint256 assets) external nonReentrant returns (uint256 shares) { + uint256 balBefore = token.balanceOf(address(this)); + token.safeTransferFrom(msg.sender, address(this), assets); + uint256 received = token.balanceOf(address(this)) - balBefore; + + shares = totalShares == 0 ? received : (received * totalShares) / _totalAssets; + _totalAssets += received; + totalShares += shares; +} +``` + +Track internal accounting and measure actual tokens received. + +### Oracle manipulation + +Spot prices are flash-loan manipulable. Prefer TWAP. + +```solidity +uint32[] memory secondsAgos = new uint32[](2); +secondsAgos[0] = 1800; +secondsAgos[1] = 0; +(int56[] memory tickCumulatives,) = IUniswapV3Pool(pool).observe(secondsAgos); +int24 twapTick = int24( + (tickCumulatives[1] - tickCumulatives[0]) / int56(uint56(30 minutes)) +); +uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(twapTick); +``` + +### Slippage protection + +Every swap path needs caller-provided slippage and a deadline. + +```solidity +function swap( + uint256 amountIn, + uint256 amountOutMin, + uint256 deadline +) external returns (uint256 amountOut) { + require(block.timestamp <= deadline, "Expired"); + amountOut = _calculateOut(amountIn); + require(amountOut >= amountOutMin, "Slippage exceeded"); + _executeSwap(amountIn, amountOut); +} +``` + +### Safe reserve math + +```solidity +import {FullMath} from "@uniswap/v3-core/contracts/libraries/FullMath.sol"; + +uint256 result = FullMath.mulDiv(a, b, c); +``` + +For large reserve math, avoid naive `a * b / c` when overflow risk exists. + +### Admin controls + +```solidity +import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol"; + +contract MyAMM is Ownable2Step { + function setFee(uint256 fee) external onlyOwner { ... } + function pause() external onlyOwner { ... } +} +``` + +Prefer explicit acceptance for ownership transfer and gate every privileged path. + +## Security Checklist + +- Reentrancy-exposed entrypoints use `nonReentrant` +- CEI ordering is respected +- Share math does not depend on raw `balanceOf(address(this))` +- ERC-20 transfers use `SafeERC20` +- Deposits measure actual tokens received +- Oracle reads use TWAP or another manipulation-resistant source +- Swaps require `amountOutMin` and `deadline` +- Overflow-sensitive reserve math uses safe primitives like `mulDiv` +- Admin functions are access-controlled +- Emergency pause exists and is tested +- Static analysis and fuzzing are run before production + +## Audit Tools + +```bash +pip install slither-analyzer +slither . --exclude-dependencies + +echidna-test . --contract YourAMM --config echidna.yaml + +forge test --fuzz-runs 10000 +``` diff --git a/skills/evm-token-decimals/SKILL.md b/skills/evm-token-decimals/SKILL.md new file mode 100644 index 00000000..845aee7a --- /dev/null +++ b/skills/evm-token-decimals/SKILL.md @@ -0,0 +1,130 @@ +--- +name: evm-token-decimals +description: Prevent silent decimal mismatch bugs across EVM chains. Covers runtime decimal lookup, chain-aware caching, bridged-token precision drift, and safe normalization for bots, dashboards, and DeFi tools. +origin: ECC direct-port adaptation +version: "1.0.0" +--- + +# EVM Token Decimals + +Silent decimal mismatches are one of the easiest ways to ship balances or USD values that are off by orders of magnitude without throwing an error. + +## When to Use + +- Reading ERC-20 balances in Python, TypeScript, or Solidity +- Calculating fiat values from on-chain balances +- Comparing token amounts across multiple EVM chains +- Handling bridged assets +- Building portfolio trackers, bots, or aggregators + +## How It Works + +Never assume stablecoins use the same decimals everywhere. Query `decimals()` at runtime, cache by `(chain_id, token_address)`, and use decimal-safe math for value calculations. + +## Examples + +### Query decimals at runtime + +```python +from decimal import Decimal +from web3 import Web3 + +ERC20_ABI = [ + {"name": "decimals", "type": "function", "inputs": [], + "outputs": [{"type": "uint8"}], "stateMutability": "view"}, + {"name": "balanceOf", "type": "function", + "inputs": [{"name": "account", "type": "address"}], + "outputs": [{"type": "uint256"}], "stateMutability": "view"}, +] + +def get_token_balance(w3: Web3, token_address: str, wallet: str) -> Decimal: + contract = w3.eth.contract( + address=Web3.to_checksum_address(token_address), + abi=ERC20_ABI, + ) + decimals = contract.functions.decimals().call() + raw = contract.functions.balanceOf(Web3.to_checksum_address(wallet)).call() + return Decimal(raw) / Decimal(10 ** decimals) +``` + +Do not hardcode `1_000_000` because a symbol usually has 6 decimals somewhere else. + +### Cache by chain and token + +```python +from functools import lru_cache + +@lru_cache(maxsize=512) +def get_decimals(chain_id: int, token_address: str) -> int: + w3 = get_web3_for_chain(chain_id) + contract = w3.eth.contract( + address=Web3.to_checksum_address(token_address), + abi=ERC20_ABI, + ) + return contract.functions.decimals().call() +``` + +### Handle odd tokens defensively + +```python +try: + decimals = contract.functions.decimals().call() +except Exception: + logging.warning( + "decimals() reverted on %s (chain %s), defaulting to 18", + token_address, + chain_id, + ) + decimals = 18 +``` + +Log the fallback and keep it visible. Old or non-standard tokens still exist. + +### Normalize to 18-decimal WAD in Solidity + +```solidity +interface IERC20Metadata { + function decimals() external view returns (uint8); +} + +function normalizeToWad(address token, uint256 amount) internal view returns (uint256) { + uint8 d = IERC20Metadata(token).decimals(); + if (d == 18) return amount; + if (d < 18) return amount * 10 ** (18 - d); + return amount / 10 ** (d - 18); +} +``` + +### TypeScript with ethers + +```typescript +import { Contract, formatUnits } from 'ethers'; + +const ERC20_ABI = [ + 'function decimals() view returns (uint8)', + 'function balanceOf(address) view returns (uint256)', +]; + +async function getBalance(provider: any, tokenAddress: string, wallet: string): Promise { + const token = new Contract(tokenAddress, ERC20_ABI, provider); + const [decimals, raw] = await Promise.all([ + token.decimals(), + token.balanceOf(wallet), + ]); + return formatUnits(raw, decimals); +} +``` + +### Quick on-chain check + +```bash +cast call "decimals()(uint8)" --rpc-url +``` + +## Rules + +- Always query `decimals()` at runtime +- Cache by chain plus token address, not symbol +- Use `Decimal`, `BigInt`, or equivalent exact math, not float +- Re-query decimals after bridging or wrapper changes +- Normalize internal accounting consistently before comparison or pricing diff --git a/skills/llm-trading-agent-security/SKILL.md b/skills/llm-trading-agent-security/SKILL.md new file mode 100644 index 00000000..45358001 --- /dev/null +++ b/skills/llm-trading-agent-security/SKILL.md @@ -0,0 +1,146 @@ +--- +name: llm-trading-agent-security +description: Security patterns for autonomous trading agents with wallet or transaction authority. Covers prompt injection, spend limits, pre-send simulation, circuit breakers, MEV protection, and key handling. +origin: ECC direct-port adaptation +version: "1.0.0" +--- + +# LLM Trading Agent Security + +Autonomous trading agents have a harsher threat model than normal LLM apps: an injection or bad tool path can turn directly into asset loss. + +## When to Use + +- Building an AI agent that signs and sends transactions +- Auditing a trading bot or on-chain execution assistant +- Designing wallet key management for an agent +- Giving an LLM access to order placement, swaps, or treasury operations + +## How It Works + +Layer the defenses. No single check is enough. Treat prompt hygiene, spend policy, simulation, execution limits, and wallet isolation as independent controls. + +## Examples + +### Treat prompt injection as a financial attack + +```python +import re + +INJECTION_PATTERNS = [ + r'ignore (previous|all) instructions', + r'new (task|directive|instruction)', + r'system prompt', + r'send .{0,50} to 0x[0-9a-fA-F]{40}', + r'transfer .{0,50} to', + r'approve .{0,50} for', +] + +def sanitize_onchain_data(text: str) -> str: + for pattern in INJECTION_PATTERNS: + if re.search(pattern, text, re.IGNORECASE): + raise ValueError(f"Potential prompt injection: {text[:100]}") + return text +``` + +Do not blindly inject token names, pair labels, webhooks, or social feeds into an execution-capable prompt. + +### Hard spend limits + +```python +from decimal import Decimal + +MAX_SINGLE_TX_USD = Decimal("500") +MAX_DAILY_SPEND_USD = Decimal("2000") + +class SpendLimitError(Exception): + pass + +class SpendLimitGuard: + def check_and_record(self, usd_amount: Decimal) -> None: + if usd_amount > MAX_SINGLE_TX_USD: + raise SpendLimitError(f"Single tx ${usd_amount} exceeds max ${MAX_SINGLE_TX_USD}") + + daily = self._get_24h_spend() + if daily + usd_amount > MAX_DAILY_SPEND_USD: + raise SpendLimitError(f"Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}") + + self._record_spend(usd_amount) +``` + +### Simulate before sending + +```python +class SlippageError(Exception): + pass + +async def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str: + sim_result = await self.w3.eth.call(tx) + + if expected_min_out is None: + raise ValueError("min_amount_out is required before send") + + actual_out = decode_uint256(sim_result) + if actual_out < expected_min_out: + raise SlippageError(f"Simulation: {actual_out} < {expected_min_out}") + + signed = self.account.sign_transaction(tx) + return await self.w3.eth.send_raw_transaction(signed.raw_transaction) +``` + +### Circuit breaker + +```python +class TradingCircuitBreaker: + MAX_CONSECUTIVE_LOSSES = 3 + MAX_HOURLY_LOSS_PCT = 0.05 + + def check(self, portfolio_value: float) -> None: + if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES: + self.halt("Too many consecutive losses") + + if self.hour_start_value <= 0: + self.halt("Invalid hour_start_value") + return + + hourly_pnl = (portfolio_value - self.hour_start_value) / self.hour_start_value + if hourly_pnl < -self.MAX_HOURLY_LOSS_PCT: + self.halt(f"Hourly PnL {hourly_pnl:.1%} below threshold") +``` + +### Wallet isolation + +```python +import os +from eth_account import Account + +private_key = os.environ.get("TRADING_WALLET_PRIVATE_KEY") +if not private_key: + raise EnvironmentError("TRADING_WALLET_PRIVATE_KEY not set") + +account = Account.from_key(private_key) +``` + +Use a dedicated hot wallet with only the required session funds. Never point the agent at a primary treasury wallet. + +### MEV and deadline protection + +```python +import time + +PRIVATE_RPC = "https://rpc.flashbots.net" +MAX_SLIPPAGE_BPS = {"stable": 10, "volatile": 50} +deadline = int(time.time()) + 60 +``` + +## Pre-Deploy Checklist + +- External data is sanitized before entering the LLM context +- Spend limits are enforced independently from model output +- Transactions are simulated before send +- `min_amount_out` is mandatory +- Circuit breakers halt on drawdown or invalid state +- Keys come from env or a secret manager, never code or logs +- Private mempool or protected routing is used when appropriate +- Slippage and deadlines are set per strategy +- All agent decisions are audit-logged, not just successful sends diff --git a/skills/nodejs-keccak256/SKILL.md b/skills/nodejs-keccak256/SKILL.md new file mode 100644 index 00000000..e885aaee --- /dev/null +++ b/skills/nodejs-keccak256/SKILL.md @@ -0,0 +1,102 @@ +--- +name: nodejs-keccak256 +description: Prevent Ethereum hashing bugs in JavaScript and TypeScript. Node's sha3-256 is NIST SHA3, not Ethereum Keccak-256, and silently breaks selectors, signatures, storage slots, and address derivation. +origin: ECC direct-port adaptation +version: "1.0.0" +--- + +# Node.js Keccak-256 + +Ethereum uses Keccak-256, not the NIST-standardized SHA3 variant exposed by Node's `crypto.createHash('sha3-256')`. + +## When to Use + +- Computing Ethereum function selectors or event topics +- Building EIP-712, signature, Merkle, or storage-slot helpers in JS/TS +- Reviewing any code that hashes Ethereum data with Node crypto directly + +## How It Works + +The two algorithms produce different outputs for the same input, and Node will not warn you. + +```javascript +import crypto from 'crypto'; +import { keccak256, toUtf8Bytes } from 'ethers'; + +const data = 'hello'; +const nistSha3 = crypto.createHash('sha3-256').update(data).digest('hex'); +const keccak = keccak256(toUtf8Bytes(data)).slice(2); + +console.log(nistSha3 === keccak); // false +``` + +## Examples + +### ethers v6 + +```typescript +import { keccak256, toUtf8Bytes, solidityPackedKeccak256, id } from 'ethers'; + +const hash = keccak256(new Uint8Array([0x01, 0x02])); +const hash2 = keccak256(toUtf8Bytes('hello')); +const topic = id('Transfer(address,address,uint256)'); +const packed = solidityPackedKeccak256( + ['address', 'uint256'], + ['0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c', 100n], +); +``` + +### viem + +```typescript +import { keccak256, toBytes } from 'viem'; + +const hash = keccak256(toBytes('hello')); +``` + +### web3.js + +```javascript +const hash = web3.utils.keccak256('hello'); +const packed = web3.utils.soliditySha3( + { type: 'address', value: '0x742d35Cc6634C0532925a3b8D4C9B569890FaC1c' }, + { type: 'uint256', value: '100' }, +); +``` + +### Common patterns + +```typescript +import { id, keccak256, AbiCoder } from 'ethers'; + +const selector = id('transfer(address,uint256)').slice(0, 10); +const typeHash = keccak256(toUtf8Bytes('Transfer(address from,address to,uint256 value)')); + +function getMappingSlot(key: string, mappingSlot: number): string { + return keccak256( + AbiCoder.defaultAbiCoder().encode(['address', 'uint256'], [key, mappingSlot]), + ); +} +``` + +### Address from public key + +```typescript +import { keccak256 } from 'ethers'; + +function pubkeyToAddress(pubkeyBytes: Uint8Array): string { + const hash = keccak256(pubkeyBytes.slice(1)); + return '0x' + hash.slice(-40); +} +``` + +### Audit your codebase + +```bash +grep -rn "createHash.*sha3" --include="*.ts" --include="*.js" --exclude-dir=node_modules . +grep -rn "keccak256" --include="*.ts" --include="*.js" . | grep -v node_modules +``` + +## Rule + +For Ethereum contexts, never use `crypto.createHash('sha3-256')`. Use Keccak-aware helpers from `ethers`, `viem`, `web3`, or another explicit Keccak implementation.