@jaimevalasek/aioson 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +456 -0
- package/CODE_OF_CONDUCT.md +12 -0
- package/CONTRIBUTING.md +13 -0
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/bin/aioson.js +4 -0
- package/docs/en/cli-reference.md +398 -0
- package/docs/en/i18n.md +52 -0
- package/docs/en/json-schemas.md +41 -0
- package/docs/en/mcp.md +56 -0
- package/docs/en/parallel.md +82 -0
- package/docs/en/qa-browser.md +339 -0
- package/docs/en/release-flow.md +22 -0
- package/docs/en/release-notes-template.md +41 -0
- package/docs/en/release.md +28 -0
- package/docs/en/schemas/agent-prompt.schema.json +17 -0
- package/docs/en/schemas/agents.schema.json +32 -0
- package/docs/en/schemas/context-validate.schema.json +36 -0
- package/docs/en/schemas/doctor.schema.json +89 -0
- package/docs/en/schemas/error.schema.json +24 -0
- package/docs/en/schemas/i18n-add.schema.json +15 -0
- package/docs/en/schemas/index.json +116 -0
- package/docs/en/schemas/info.schema.json +39 -0
- package/docs/en/schemas/init.schema.json +48 -0
- package/docs/en/schemas/install.schema.json +60 -0
- package/docs/en/schemas/locale-apply.schema.json +30 -0
- package/docs/en/schemas/mcp-doctor.schema.json +95 -0
- package/docs/en/schemas/mcp-init.schema.json +122 -0
- package/docs/en/schemas/package-test.schema.json +24 -0
- package/docs/en/schemas/parallel-assign.schema.json +57 -0
- package/docs/en/schemas/parallel-doctor.schema.json +86 -0
- package/docs/en/schemas/parallel-init.schema.json +53 -0
- package/docs/en/schemas/parallel-status.schema.json +94 -0
- package/docs/en/schemas/setup-context.schema.json +39 -0
- package/docs/en/schemas/smoke.schema.json +23 -0
- package/docs/en/schemas/update.schema.json +48 -0
- package/docs/en/schemas/workflow-plan.schema.json +30 -0
- package/docs/en/web3.md +54 -0
- package/docs/pt/README.md +46 -0
- package/docs/pt/advisor-spec.md +335 -0
- package/docs/pt/agentes.md +453 -0
- package/docs/pt/cenarios.md +1230 -0
- package/docs/pt/clientes-ai.md +224 -0
- package/docs/pt/comandos-cli.md +511 -0
- package/docs/pt/genome-3.0-spec.md +296 -0
- package/docs/pt/guia-engineer.md +226 -0
- package/docs/pt/inicio-rapido.md +138 -0
- package/docs/pt/profiler-system.md +214 -0
- package/docs/pt/runtime-observability.md +72 -0
- package/docs/pt/squad-genoma.md +777 -0
- package/docs/pt/web3.md +797 -0
- package/docs/testing/genome-2.0-manual-regression.md +23 -0
- package/docs/testing/genome-2.0-matrix.md +36 -0
- package/docs/testing/genome-2.0-rollout.md +184 -0
- package/package.json +50 -0
- package/src/agents.js +56 -0
- package/src/cli.js +497 -0
- package/src/commands/agents.js +142 -0
- package/src/commands/cloud.js +1767 -0
- package/src/commands/config.js +90 -0
- package/src/commands/context-validate.js +91 -0
- package/src/commands/doctor.js +123 -0
- package/src/commands/genome-doctor.js +41 -0
- package/src/commands/genome-migrate.js +49 -0
- package/src/commands/i18n-add.js +56 -0
- package/src/commands/info.js +41 -0
- package/src/commands/init.js +75 -0
- package/src/commands/install.js +68 -0
- package/src/commands/locale-apply.js +51 -0
- package/src/commands/locale-diff.js +126 -0
- package/src/commands/mcp-doctor.js +406 -0
- package/src/commands/mcp-init.js +379 -0
- package/src/commands/package-e2e.js +273 -0
- package/src/commands/parallel-assign.js +403 -0
- package/src/commands/parallel-doctor.js +437 -0
- package/src/commands/parallel-init.js +249 -0
- package/src/commands/parallel-status.js +290 -0
- package/src/commands/qa-doctor.js +185 -0
- package/src/commands/qa-init.js +161 -0
- package/src/commands/qa-report.js +58 -0
- package/src/commands/qa-run.js +873 -0
- package/src/commands/qa-scan.js +337 -0
- package/src/commands/runtime.js +948 -0
- package/src/commands/scan-project.js +1107 -0
- package/src/commands/setup-context.js +650 -0
- package/src/commands/smoke.js +426 -0
- package/src/commands/squad-doctor.js +358 -0
- package/src/commands/squad-export.js +46 -0
- package/src/commands/squad-pipeline.js +97 -0
- package/src/commands/squad-repair-genomes.js +39 -0
- package/src/commands/squad-status.js +424 -0
- package/src/commands/squad-validate.js +230 -0
- package/src/commands/test-agents.js +194 -0
- package/src/commands/update.js +55 -0
- package/src/commands/workflow-next.js +594 -0
- package/src/commands/workflow-plan.js +108 -0
- package/src/constants.js +314 -0
- package/src/context-parse-reason.js +22 -0
- package/src/context-writer.js +150 -0
- package/src/context.js +217 -0
- package/src/detector.js +261 -0
- package/src/doctor.js +289 -0
- package/src/execution-gateway.js +461 -0
- package/src/genome-files.js +198 -0
- package/src/genome-format.js +442 -0
- package/src/genome-schema.js +215 -0
- package/src/genomes/bindings.js +281 -0
- package/src/genomes.js +467 -0
- package/src/i18n/index.js +103 -0
- package/src/i18n/messages/en.js +784 -0
- package/src/i18n/messages/es.js +718 -0
- package/src/i18n/messages/fr.js +725 -0
- package/src/i18n/messages/pt-BR.js +818 -0
- package/src/i18n/scaffold.js +64 -0
- package/src/installer.js +232 -0
- package/src/lib/genomes/compat.js +206 -0
- package/src/lib/genomes/migrate.js +90 -0
- package/src/lib/squads/genome-repair.js +49 -0
- package/src/locales.js +84 -0
- package/src/onboarding.js +305 -0
- package/src/parser.js +53 -0
- package/src/prompt-tool.js +20 -0
- package/src/qa-html-report.js +472 -0
- package/src/runtime-store.js +1527 -0
- package/src/squads/apply-genome.js +21 -0
- package/src/squads/genome-binding-service.js +154 -0
- package/src/updater.js +32 -0
- package/src/utils.js +46 -0
- package/src/version.js +50 -0
- package/template/.aioson/advisors/.gitkeep +1 -0
- package/template/.aioson/agents/analyst.md +225 -0
- package/template/.aioson/agents/architect.md +221 -0
- package/template/.aioson/agents/dev.md +201 -0
- package/template/.aioson/agents/discovery-design-doc.md +196 -0
- package/template/.aioson/agents/genoma.md +300 -0
- package/template/.aioson/agents/orchestrator.md +107 -0
- package/template/.aioson/agents/pm.md +89 -0
- package/template/.aioson/agents/product.md +361 -0
- package/template/.aioson/agents/profiler-enricher.md +266 -0
- package/template/.aioson/agents/profiler-forge.md +188 -0
- package/template/.aioson/agents/profiler-researcher.md +245 -0
- package/template/.aioson/agents/qa.md +344 -0
- package/template/.aioson/agents/setup.md +381 -0
- package/template/.aioson/agents/squad.md +837 -0
- package/template/.aioson/agents/ux-ui.md +416 -0
- package/template/.aioson/config.md +56 -0
- package/template/.aioson/context/.gitkeep +0 -0
- package/template/.aioson/context/parallel/.gitkeep +0 -0
- package/template/.aioson/context/spec.md.template +37 -0
- package/template/.aioson/genomas/.gitkeep +0 -0
- package/template/.aioson/locales/en/agents/analyst.md +214 -0
- package/template/.aioson/locales/en/agents/architect.md +210 -0
- package/template/.aioson/locales/en/agents/dev.md +187 -0
- package/template/.aioson/locales/en/agents/discovery-design-doc.md +27 -0
- package/template/.aioson/locales/en/agents/genoma.md +212 -0
- package/template/.aioson/locales/en/agents/orchestrator.md +105 -0
- package/template/.aioson/locales/en/agents/pm.md +77 -0
- package/template/.aioson/locales/en/agents/product.md +310 -0
- package/template/.aioson/locales/en/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/en/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/en/agents/qa.md +214 -0
- package/template/.aioson/locales/en/agents/setup.md +342 -0
- package/template/.aioson/locales/en/agents/squad.md +247 -0
- package/template/.aioson/locales/en/agents/ux-ui.md +320 -0
- package/template/.aioson/locales/es/agents/analyst.md +203 -0
- package/template/.aioson/locales/es/agents/architect.md +208 -0
- package/template/.aioson/locales/es/agents/dev.md +183 -0
- package/template/.aioson/locales/es/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/es/agents/genoma.md +102 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/es/agents/pm.md +81 -0
- package/template/.aioson/locales/es/agents/product.md +310 -0
- package/template/.aioson/locales/es/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/es/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/es/agents/qa.md +163 -0
- package/template/.aioson/locales/es/agents/setup.md +347 -0
- package/template/.aioson/locales/es/agents/squad.md +247 -0
- package/template/.aioson/locales/es/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/fr/agents/analyst.md +203 -0
- package/template/.aioson/locales/fr/agents/architect.md +208 -0
- package/template/.aioson/locales/fr/agents/dev.md +183 -0
- package/template/.aioson/locales/fr/agents/discovery-design-doc.md +19 -0
- package/template/.aioson/locales/fr/agents/genoma.md +102 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/fr/agents/pm.md +81 -0
- package/template/.aioson/locales/fr/agents/product.md +310 -0
- package/template/.aioson/locales/fr/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/fr/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/fr/agents/qa.md +163 -0
- package/template/.aioson/locales/fr/agents/setup.md +347 -0
- package/template/.aioson/locales/fr/agents/squad.md +247 -0
- package/template/.aioson/locales/fr/agents/ux-ui.md +201 -0
- package/template/.aioson/locales/pt-BR/agents/analyst.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/architect.md +213 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +198 -0
- package/template/.aioson/locales/pt-BR/agents/genoma.md +297 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +108 -0
- package/template/.aioson/locales/pt-BR/agents/pm.md +81 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +316 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/qa.md +217 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +371 -0
- package/template/.aioson/locales/pt-BR/agents/squad.md +772 -0
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +322 -0
- package/template/.aioson/mcp/servers.md +24 -0
- package/template/.aioson/profiler-reports/.gitkeep +1 -0
- package/template/.aioson/schemas/content-blueprint.schema.json +30 -0
- package/template/.aioson/schemas/genome-meta.schema.json +150 -0
- package/template/.aioson/schemas/genome.schema.json +115 -0
- package/template/.aioson/schemas/readiness.schema.json +27 -0
- package/template/.aioson/schemas/squad-blueprint.schema.json +172 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +276 -0
- package/template/.aioson/skills/dynamic/README.md +30 -0
- package/template/.aioson/skills/dynamic/cardano-docs.md +16 -0
- package/template/.aioson/skills/dynamic/ethereum-docs.md +17 -0
- package/template/.aioson/skills/dynamic/flux-ui-docs.md +13 -0
- package/template/.aioson/skills/dynamic/laravel-docs.md +41 -0
- package/template/.aioson/skills/dynamic/npm-packages.md +16 -0
- package/template/.aioson/skills/dynamic/solana-docs.md +16 -0
- package/template/.aioson/skills/references/premium-command-center-ui/master-application-prompt.md +79 -0
- package/template/.aioson/skills/references/premium-command-center-ui/operational-ux-playbook.md +253 -0
- package/template/.aioson/skills/references/premium-command-center-ui/quality-validation-checklist.md +82 -0
- package/template/.aioson/skills/references/premium-command-center-ui/visual-system-and-component-patterns.md +270 -0
- package/template/.aioson/skills/static/django-patterns.md +342 -0
- package/template/.aioson/skills/static/fastapi-patterns.md +344 -0
- package/template/.aioson/skills/static/filament-patterns.md +267 -0
- package/template/.aioson/skills/static/flux-ui-components.md +262 -0
- package/template/.aioson/skills/static/git-conventions.md +227 -0
- package/template/.aioson/skills/static/interface-design.md +372 -0
- package/template/.aioson/skills/static/jetstream-setup.md +200 -0
- package/template/.aioson/skills/static/laravel-conventions.md +491 -0
- package/template/.aioson/skills/static/nextjs-patterns.md +321 -0
- package/template/.aioson/skills/static/node-express-patterns.md +317 -0
- package/template/.aioson/skills/static/node-typescript-patterns.md +282 -0
- package/template/.aioson/skills/static/premium-command-center-ui.md +190 -0
- package/template/.aioson/skills/static/rails-conventions.md +307 -0
- package/template/.aioson/skills/static/react-motion-patterns.md +577 -0
- package/template/.aioson/skills/static/static-html-patterns.md +1935 -0
- package/template/.aioson/skills/static/tall-stack-patterns.md +286 -0
- package/template/.aioson/skills/static/ui-ux-modern.md +75 -0
- package/template/.aioson/skills/static/web3-cardano-patterns.md +337 -0
- package/template/.aioson/skills/static/web3-ethereum-patterns.md +310 -0
- package/template/.aioson/skills/static/web3-security-checklist.md +284 -0
- package/template/.aioson/skills/static/web3-solana-patterns.md +324 -0
- package/template/.aioson/squads/.artisan/.gitkeep +0 -0
- package/template/.aioson/squads/.gitkeep +0 -0
- package/template/.aioson/squads/memory.md +5 -0
- package/template/.aioson/tasks/squad-analyze.md +83 -0
- package/template/.aioson/tasks/squad-create.md +99 -0
- package/template/.aioson/tasks/squad-design.md +100 -0
- package/template/.aioson/tasks/squad-export.md +20 -0
- package/template/.aioson/tasks/squad-extend.md +68 -0
- package/template/.aioson/tasks/squad-pipeline.md +122 -0
- package/template/.aioson/tasks/squad-repair.md +85 -0
- package/template/.aioson/tasks/squad-validate.md +58 -0
- package/template/.aioson/templates/squads/content-basic/template.json +21 -0
- package/template/.aioson/templates/squads/media-channel/template.json +24 -0
- package/template/.aioson/templates/squads/research-analysis/template.json +22 -0
- package/template/.aioson/templates/squads/software-delivery/template.json +21 -0
- package/template/.claude/commands/aioson/analyst.md +5 -0
- package/template/.claude/commands/aioson/architect.md +5 -0
- package/template/.claude/commands/aioson/dev.md +5 -0
- package/template/.claude/commands/aioson/orchestrator.md +5 -0
- package/template/.claude/commands/aioson/pm.md +5 -0
- package/template/.claude/commands/aioson/qa.md +5 -0
- package/template/.claude/commands/aioson/setup.md +5 -0
- package/template/.claude/commands/aioson/ux-ui.md +5 -0
- package/template/.gemini/GEMINI.md +10 -0
- package/template/.gemini/commands/aios-analyst.toml +4 -0
- package/template/.gemini/commands/aios-architect.toml +7 -0
- package/template/.gemini/commands/aios-dev.toml +8 -0
- package/template/.gemini/commands/aios-discovery-design-doc.toml +4 -0
- package/template/.gemini/commands/aios-orchestrator.toml +8 -0
- package/template/.gemini/commands/aios-pm.toml +8 -0
- package/template/.gemini/commands/aios-product.toml +4 -0
- package/template/.gemini/commands/aios-qa.toml +6 -0
- package/template/.gemini/commands/aios-setup.toml +3 -0
- package/template/.gemini/commands/aios-ux-ui.toml +8 -0
- package/template/AGENTS.md +67 -0
- package/template/CLAUDE.md +31 -0
- package/template/OPENCODE.md +24 -0
- package/template/aioson-models.json +40 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# Web3 Ethereum Patterns
|
|
2
|
+
|
|
3
|
+
> Solidity, Hardhat/Foundry, and frontend integration. Security first, gas second.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Project structure (Hardhat monorepo)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
contracts/
|
|
11
|
+
core/
|
|
12
|
+
Protocol.sol ← main contract
|
|
13
|
+
interfaces/
|
|
14
|
+
IProtocol.sol ← interface first, implementation second
|
|
15
|
+
tokens/
|
|
16
|
+
MyToken.sol ← ERC-20/721/1155
|
|
17
|
+
utils/
|
|
18
|
+
Math.sol
|
|
19
|
+
Pausable.sol
|
|
20
|
+
mocks/
|
|
21
|
+
MockToken.sol ← test doubles only, never in production
|
|
22
|
+
scripts/
|
|
23
|
+
deploy/
|
|
24
|
+
01_deploy_token.ts
|
|
25
|
+
02_deploy_protocol.ts
|
|
26
|
+
verify.ts ← Etherscan verification
|
|
27
|
+
test/
|
|
28
|
+
Protocol.test.ts
|
|
29
|
+
integration/
|
|
30
|
+
fork.test.ts ← mainnet fork tests
|
|
31
|
+
hardhat.config.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Security patterns — always apply
|
|
37
|
+
|
|
38
|
+
### 1. Reentrancy guard (any function that sends ETH or calls external contracts)
|
|
39
|
+
|
|
40
|
+
```solidity
|
|
41
|
+
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
42
|
+
|
|
43
|
+
contract Marketplace is ReentrancyGuard {
|
|
44
|
+
// WRONG — state change after external call
|
|
45
|
+
function withdraw() external {
|
|
46
|
+
uint256 amount = balances[msg.sender];
|
|
47
|
+
payable(msg.sender).transfer(amount); // external call first = reentrancy vector
|
|
48
|
+
balances[msg.sender] = 0; // state change after = too late
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// RIGHT — CEI pattern (Checks → Effects → Interactions)
|
|
52
|
+
function withdraw() external nonReentrant {
|
|
53
|
+
uint256 amount = balances[msg.sender]; // Check
|
|
54
|
+
balances[msg.sender] = 0; // Effect (state change BEFORE external call)
|
|
55
|
+
payable(msg.sender).transfer(amount); // Interaction (external call LAST)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Pull over push for payments
|
|
61
|
+
|
|
62
|
+
```solidity
|
|
63
|
+
// WRONG — push: sending ETH to multiple addresses in a loop
|
|
64
|
+
function distributeRewards(address[] calldata recipients, uint256[] calldata amounts) external {
|
|
65
|
+
for (uint i = 0; i < recipients.length; i++) {
|
|
66
|
+
payable(recipients[i]).transfer(amounts[i]); // one revert blocks all
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// RIGHT — pull: let recipients withdraw their own funds
|
|
71
|
+
mapping(address => uint256) public pendingWithdrawals;
|
|
72
|
+
|
|
73
|
+
function claimReward() external nonReentrant {
|
|
74
|
+
uint256 amount = pendingWithdrawals[msg.sender];
|
|
75
|
+
require(amount > 0, "Nothing to claim");
|
|
76
|
+
pendingWithdrawals[msg.sender] = 0;
|
|
77
|
+
(bool success, ) = payable(msg.sender).call{ value: amount }("");
|
|
78
|
+
require(success, "Transfer failed");
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. Access control
|
|
83
|
+
|
|
84
|
+
```solidity
|
|
85
|
+
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
86
|
+
|
|
87
|
+
contract Protocol is AccessControl {
|
|
88
|
+
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
|
|
89
|
+
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
|
|
90
|
+
|
|
91
|
+
constructor(address admin) {
|
|
92
|
+
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
93
|
+
_grantRole(ADMIN_ROLE, admin);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function pause() external onlyRole(ADMIN_ROLE) { ... }
|
|
97
|
+
function setFee(uint256 fee) external onlyRole(OPERATOR_ROLE) { ... }
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Integer arithmetic — use 18-decimal fixed point
|
|
102
|
+
|
|
103
|
+
```solidity
|
|
104
|
+
// WRONG — division before multiplication loses precision
|
|
105
|
+
uint256 fee = (amount / 100) * 3; // loses precision if amount < 100
|
|
106
|
+
|
|
107
|
+
// RIGHT — multiply first, divide last
|
|
108
|
+
uint256 FEE_BPS = 300; // 3% in basis points
|
|
109
|
+
uint256 fee = (amount * FEE_BPS) / 10_000;
|
|
110
|
+
|
|
111
|
+
// Use constants for magic numbers
|
|
112
|
+
uint256 public constant MAX_FEE_BPS = 1_000; // 10% max
|
|
113
|
+
uint256 public constant PRECISION = 1e18;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 5. Emit events for all state changes
|
|
117
|
+
|
|
118
|
+
```solidity
|
|
119
|
+
event Deposited(address indexed user, uint256 amount, uint256 timestamp);
|
|
120
|
+
event Withdrawn(address indexed user, uint256 amount);
|
|
121
|
+
event FeeUpdated(uint256 oldFee, uint256 newFee, address updatedBy);
|
|
122
|
+
|
|
123
|
+
function deposit() external payable {
|
|
124
|
+
require(msg.value > 0, "Amount must be positive");
|
|
125
|
+
balances[msg.sender] += msg.value;
|
|
126
|
+
emit Deposited(msg.sender, msg.value, block.timestamp);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Gas optimization
|
|
133
|
+
|
|
134
|
+
```solidity
|
|
135
|
+
// Pack struct fields to use fewer storage slots (32 bytes each)
|
|
136
|
+
// WRONG — 3 slots
|
|
137
|
+
struct BadPacking {
|
|
138
|
+
uint256 amount; // slot 0
|
|
139
|
+
address owner; // slot 1 (20 bytes, wastes 12)
|
|
140
|
+
uint256 timestamp; // slot 2
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// RIGHT — 2 slots
|
|
144
|
+
struct GoodPacking {
|
|
145
|
+
uint256 amount; // slot 0
|
|
146
|
+
address owner; // slot 1 (20 bytes)
|
|
147
|
+
uint96 timestamp; // slot 1 (12 bytes — fits in same slot as owner)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Use calldata instead of memory for read-only external function params
|
|
151
|
+
function processItems(uint256[] calldata items) external view returns (uint256) { ... }
|
|
152
|
+
|
|
153
|
+
// Cache storage reads in local variables (SLOAD costs 100+ gas)
|
|
154
|
+
function calculate() external view returns (uint256) {
|
|
155
|
+
uint256 _balance = balance; // one SLOAD
|
|
156
|
+
return _balance * _balance; // two local reads, not two SLOADs
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Use custom errors instead of string revert (saves gas)
|
|
160
|
+
error InsufficientBalance(uint256 available, uint256 required);
|
|
161
|
+
|
|
162
|
+
function withdraw(uint256 amount) external {
|
|
163
|
+
if (balances[msg.sender] < amount) {
|
|
164
|
+
revert InsufficientBalance(balances[msg.sender], amount);
|
|
165
|
+
}
|
|
166
|
+
// ...
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Hardhat testing
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import { ethers } from 'hardhat';
|
|
176
|
+
import { expect } from 'chai';
|
|
177
|
+
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
|
|
178
|
+
|
|
179
|
+
async function deployFixture() {
|
|
180
|
+
const [owner, alice, bob] = await ethers.getSigners();
|
|
181
|
+
const Protocol = await ethers.getContractFactory('Protocol');
|
|
182
|
+
const protocol = await Protocol.deploy(owner.address);
|
|
183
|
+
return { protocol, owner, alice, bob };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
describe('Protocol', () => {
|
|
187
|
+
it('allows deposits and tracks balances', async () => {
|
|
188
|
+
const { protocol, alice } = await loadFixture(deployFixture);
|
|
189
|
+
const amount = ethers.parseEther('1.0');
|
|
190
|
+
|
|
191
|
+
await protocol.connect(alice).deposit({ value: amount });
|
|
192
|
+
expect(await protocol.balances(alice.address)).to.equal(amount);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('prevents reentrancy on withdraw', async () => {
|
|
196
|
+
const { protocol, alice } = await loadFixture(deployFixture);
|
|
197
|
+
const MaliciousContract = await ethers.getContractFactory('MockReentrant');
|
|
198
|
+
const attacker = await MaliciousContract.deploy(await protocol.getAddress());
|
|
199
|
+
|
|
200
|
+
await protocol.connect(alice).deposit({ value: ethers.parseEther('1') });
|
|
201
|
+
await expect(attacker.attack()).to.be.revertedWith('ReentrancyGuard: reentrant call');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('emits Deposited event', async () => {
|
|
205
|
+
const { protocol, alice } = await loadFixture(deployFixture);
|
|
206
|
+
const amount = ethers.parseEther('0.5');
|
|
207
|
+
|
|
208
|
+
await expect(protocol.connect(alice).deposit({ value: amount }))
|
|
209
|
+
.to.emit(protocol, 'Deposited')
|
|
210
|
+
.withArgs(alice.address, amount, anyValue);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Frontend integration (wagmi v2)
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
// lib/contracts.ts — ABI and address registry
|
|
221
|
+
export const PROTOCOL_ADDRESS = process.env.NEXT_PUBLIC_PROTOCOL_ADDRESS as `0x${string}`;
|
|
222
|
+
export { abi as PROTOCOL_ABI } from './abis/Protocol.json';
|
|
223
|
+
|
|
224
|
+
// Read contract data
|
|
225
|
+
import { useReadContract } from 'wagmi';
|
|
226
|
+
|
|
227
|
+
function UserBalance({ address }: { address: `0x${string}` }) {
|
|
228
|
+
const { data: balance, isLoading } = useReadContract({
|
|
229
|
+
address: PROTOCOL_ADDRESS,
|
|
230
|
+
abi: PROTOCOL_ABI,
|
|
231
|
+
functionName: 'balances',
|
|
232
|
+
args: [address],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
if (isLoading) return <Skeleton />;
|
|
236
|
+
return <p>{formatEther(balance ?? 0n)} ETH</p>;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Write contract
|
|
240
|
+
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
|
|
241
|
+
import { parseEther } from 'viem';
|
|
242
|
+
|
|
243
|
+
function DepositButton() {
|
|
244
|
+
const { writeContract, data: hash, isPending } = useWriteContract();
|
|
245
|
+
const { isLoading: isConfirming } = useWaitForTransactionReceipt({ hash });
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<button
|
|
249
|
+
disabled={isPending || isConfirming}
|
|
250
|
+
onClick={() => writeContract({
|
|
251
|
+
address: PROTOCOL_ADDRESS,
|
|
252
|
+
abi: PROTOCOL_ABI,
|
|
253
|
+
functionName: 'deposit',
|
|
254
|
+
value: parseEther('0.1'),
|
|
255
|
+
})}
|
|
256
|
+
>
|
|
257
|
+
{isPending ? 'Waiting for wallet...' : isConfirming ? 'Confirming...' : 'Deposit 0.1 ETH'}
|
|
258
|
+
</button>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Deployment scripts
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
// scripts/deploy/01_deploy_protocol.ts
|
|
269
|
+
import { ethers, run } from 'hardhat';
|
|
270
|
+
|
|
271
|
+
async function main() {
|
|
272
|
+
const [deployer] = await ethers.getSigners();
|
|
273
|
+
console.log(`Deploying with: ${deployer.address}`);
|
|
274
|
+
console.log(`Balance: ${ethers.formatEther(await ethers.provider.getBalance(deployer.address))} ETH`);
|
|
275
|
+
|
|
276
|
+
const Protocol = await ethers.getContractFactory('Protocol');
|
|
277
|
+
const protocol = await Protocol.deploy(deployer.address);
|
|
278
|
+
await protocol.waitForDeployment();
|
|
279
|
+
|
|
280
|
+
const address = await protocol.getAddress();
|
|
281
|
+
console.log(`Protocol deployed to: ${address}`);
|
|
282
|
+
|
|
283
|
+
// Verify on Etherscan
|
|
284
|
+
if (process.env.ETHERSCAN_API_KEY) {
|
|
285
|
+
await new Promise(r => setTimeout(r, 30_000)); // wait for indexing
|
|
286
|
+
await run('verify:verify', { address, constructorArguments: [deployer.address] });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
main().catch((err) => { console.error(err); process.exit(1); });
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## ALWAYS
|
|
296
|
+
- CEI pattern (Checks → Effects → Interactions) in every state-changing function
|
|
297
|
+
- `ReentrancyGuard` on functions that send ETH or call external contracts
|
|
298
|
+
- Pull pattern for ETH payments
|
|
299
|
+
- `AccessControl` for role-based permissions
|
|
300
|
+
- Custom errors over string reverts
|
|
301
|
+
- `loadFixture` in tests for consistent state
|
|
302
|
+
- Emit events for every important state change
|
|
303
|
+
|
|
304
|
+
## NEVER
|
|
305
|
+
- `transfer()` or `send()` in public functions (use `.call{value: ...}("")`)
|
|
306
|
+
- Division before multiplication (precision loss)
|
|
307
|
+
- Unbounded loops that could hit block gas limit
|
|
308
|
+
- `block.timestamp` for critical time-based logic (miners can manipulate ±15s)
|
|
309
|
+
- Deploy without testing on a fork of mainnet for DeFi integrations
|
|
310
|
+
- `selfdestruct` — deprecated and dangerous
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Web3 Security Checklist
|
|
2
|
+
|
|
3
|
+
> Every vulnerability on this list has drained real funds. Check all of them before mainnet.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Critical: Reentrancy
|
|
8
|
+
|
|
9
|
+
**What:** An external contract calls back into your contract before the first execution completes.
|
|
10
|
+
|
|
11
|
+
```solidity
|
|
12
|
+
// VULNERABLE
|
|
13
|
+
function withdraw() external {
|
|
14
|
+
uint256 amount = balances[msg.sender];
|
|
15
|
+
(bool success,) = msg.sender.call{value: amount}(""); // attacker re-enters here
|
|
16
|
+
require(success);
|
|
17
|
+
balances[msg.sender] = 0; // never reached in attack
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// SAFE — CEI pattern + ReentrancyGuard
|
|
21
|
+
function withdraw() external nonReentrant {
|
|
22
|
+
uint256 amount = balances[msg.sender];
|
|
23
|
+
balances[msg.sender] = 0; // effect first
|
|
24
|
+
(bool success,) = msg.sender.call{value: amount}(""); // interaction last
|
|
25
|
+
require(success, "Transfer failed");
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Checklist:**
|
|
30
|
+
- [ ] All external calls come AFTER state changes (CEI pattern)
|
|
31
|
+
- [ ] `ReentrancyGuard` on all functions making external calls
|
|
32
|
+
- [ ] `nonReentrant` on withdraw, claim, and swap functions
|
|
33
|
+
- [ ] Cross-function reentrancy checked (function A → function B → reenter A)
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Critical: Access Control
|
|
38
|
+
|
|
39
|
+
**What:** Missing or bypassed authorization on privileged functions.
|
|
40
|
+
|
|
41
|
+
```solidity
|
|
42
|
+
// VULNERABLE — anyone can drain
|
|
43
|
+
function withdrawFees() external {
|
|
44
|
+
payable(msg.sender).transfer(address(this).balance);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// SAFE
|
|
48
|
+
function withdrawFees() external onlyRole(TREASURY_ROLE) {
|
|
49
|
+
payable(treasury).transfer(address(this).balance);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Checklist:**
|
|
54
|
+
- [ ] Every state-changing function has explicit caller validation
|
|
55
|
+
- [ ] Admin functions use `AccessControl` roles, not `onlyOwner` alone
|
|
56
|
+
- [ ] Constructor sets roles explicitly — no open initialization window
|
|
57
|
+
- [ ] Role transfers require two-step confirmation (propose + accept)
|
|
58
|
+
- [ ] `renounceRole` / `transferOwnership` tested and working as expected
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Critical: Integer Overflow / Underflow
|
|
63
|
+
|
|
64
|
+
Pre-Solidity 0.8: integers wrap silently. Post-0.8: revert on overflow by default.
|
|
65
|
+
|
|
66
|
+
```solidity
|
|
67
|
+
// Post-0.8 — safe by default, but watch unchecked blocks
|
|
68
|
+
unchecked {
|
|
69
|
+
// No overflow protection here — only use when you've proven safety
|
|
70
|
+
total += amount;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Safe arithmetic pattern for complex calculations
|
|
74
|
+
uint256 fee = Math.mulDiv(amount, feeBps, 10_000); // OpenZeppelin safe mulDiv
|
|
75
|
+
|
|
76
|
+
// Precision loss from division order
|
|
77
|
+
uint256 bad = (amount / 100) * 3; // loses decimals
|
|
78
|
+
uint256 good = (amount * 3) / 100; // multiply first
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Checklist:**
|
|
82
|
+
- [ ] Solidity version ≥ 0.8.0 (overflow protection by default)
|
|
83
|
+
- [ ] All `unchecked` blocks justified with a comment explaining why it's safe
|
|
84
|
+
- [ ] Multiplication happens before division in all fee calculations
|
|
85
|
+
- [ ] No precision loss in basis-points calculations (use `mulDiv`)
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Critical: Oracle Manipulation
|
|
90
|
+
|
|
91
|
+
**What:** Price oracles can be manipulated, especially spot prices read in a single block.
|
|
92
|
+
|
|
93
|
+
```solidity
|
|
94
|
+
// VULNERABLE — spot price from AMM, manipulable via flash loan
|
|
95
|
+
function getPrice() external view returns (uint256) {
|
|
96
|
+
return IUniswapV2Pair(pair).getReserves(); // single-block price
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// SAFE — use time-weighted average price (TWAP)
|
|
100
|
+
function getPrice() external view returns (uint256) {
|
|
101
|
+
return IUniswapV3Pool(pool).observe(twapInterval); // time-weighted
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ALSO SAFE — Chainlink oracle with staleness check
|
|
105
|
+
function getPrice() external view returns (uint256) {
|
|
106
|
+
(, int256 price,, uint256 updatedAt,) = AggregatorV3Interface(feed).latestRoundData();
|
|
107
|
+
require(block.timestamp - updatedAt < 3600, "Oracle: stale price");
|
|
108
|
+
require(price > 0, "Oracle: invalid price");
|
|
109
|
+
return uint256(price);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Checklist:**
|
|
114
|
+
- [ ] No spot prices from AMMs used for liquidations, borrowing, or minting
|
|
115
|
+
- [ ] TWAP or Chainlink feeds for all price-sensitive logic
|
|
116
|
+
- [ ] Chainlink answers validated: `updatedAt`, `answeredInRound`, `price > 0`
|
|
117
|
+
- [ ] Circuit breaker for extreme price deviations (±X% from last round)
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Critical: Flash Loan Attacks
|
|
122
|
+
|
|
123
|
+
**What:** An attacker borrows a large amount, manipulates your protocol's state, and repays — all in one transaction.
|
|
124
|
+
|
|
125
|
+
```solidity
|
|
126
|
+
// Pattern: protect against flash loan price manipulation
|
|
127
|
+
modifier noFlashLoan() {
|
|
128
|
+
require(tx.origin == msg.sender, "Flash loans not allowed");
|
|
129
|
+
_;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Better: use internal accounting, not external balances
|
|
133
|
+
// VULNERABLE — reads current balance (flash-loanable)
|
|
134
|
+
function getReserves() view returns (uint256) {
|
|
135
|
+
return IERC20(token).balanceOf(address(this));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// SAFE — uses tracked internal accounting
|
|
139
|
+
mapping(address => uint256) private reserves;
|
|
140
|
+
function getReserves(address token) view returns (uint256) {
|
|
141
|
+
return reserves[token]; // updated only after safe deposits
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Checklist:**
|
|
146
|
+
- [ ] State does not depend on `balanceOf` for protocol invariants
|
|
147
|
+
- [ ] Invariants checked before and after complex operations
|
|
148
|
+
- [ ] Slippage / deviation limits on swaps and liquidations
|
|
149
|
+
- [ ] Test with foundry `vm.deal` simulating flash loan amounts
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## High: Front-Running
|
|
154
|
+
|
|
155
|
+
**What:** Miners or MEV bots see your pending transaction and insert their own first.
|
|
156
|
+
|
|
157
|
+
```solidity
|
|
158
|
+
// Vulnerable: deadline and slippage in swap
|
|
159
|
+
function swap(uint256 amountIn) external {
|
|
160
|
+
// No deadline, no slippage — sandwich attack trivial
|
|
161
|
+
uint256 out = pool.swap(amountIn);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Protected: explicit slippage and deadline
|
|
165
|
+
function swap(
|
|
166
|
+
uint256 amountIn,
|
|
167
|
+
uint256 minAmountOut,
|
|
168
|
+
uint256 deadline
|
|
169
|
+
) external {
|
|
170
|
+
require(block.timestamp <= deadline, "Expired");
|
|
171
|
+
uint256 out = pool.swap(amountIn);
|
|
172
|
+
require(out >= minAmountOut, "Slippage too high");
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Checklist:**
|
|
177
|
+
- [ ] All user-facing swaps include `minAmountOut` and `deadline`
|
|
178
|
+
- [ ] Governance votes have timelocks (≥48h for critical changes)
|
|
179
|
+
- [ ] Commit-reveal scheme for any sensitive on-chain randomness
|
|
180
|
+
- [ ] Consider using Flashbots Protect RPC for sensitive deployments
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## High: Signature Replay
|
|
185
|
+
|
|
186
|
+
**What:** A valid signed message is resubmitted to the same or a different contract.
|
|
187
|
+
|
|
188
|
+
```solidity
|
|
189
|
+
// VULNERABLE — no nonce, no chain ID
|
|
190
|
+
function execute(bytes32 hash, bytes calldata sig) external {
|
|
191
|
+
address signer = ECDSA.recover(hash, sig);
|
|
192
|
+
require(signer == admin);
|
|
193
|
+
// attacker replays this on another chain or calls again
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// SAFE — EIP-712 with nonce and chainId
|
|
197
|
+
function execute(
|
|
198
|
+
address target,
|
|
199
|
+
uint256 value,
|
|
200
|
+
uint256 nonce,
|
|
201
|
+
uint256 deadline,
|
|
202
|
+
bytes calldata sig
|
|
203
|
+
) external {
|
|
204
|
+
require(block.timestamp <= deadline, "Expired");
|
|
205
|
+
require(nonces[msg.sender] == nonce, "Invalid nonce");
|
|
206
|
+
|
|
207
|
+
bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
|
|
208
|
+
EXECUTE_TYPEHASH, target, value, nonce, deadline
|
|
209
|
+
)));
|
|
210
|
+
|
|
211
|
+
require(ECDSA.recover(digest, sig) == admin, "Invalid signature");
|
|
212
|
+
nonces[msg.sender]++;
|
|
213
|
+
// execute...
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Checklist:**
|
|
218
|
+
- [ ] All permit/signature functions use EIP-712 typed data
|
|
219
|
+
- [ ] Nonce incremented on every use
|
|
220
|
+
- [ ] `chainId` included in the domain separator
|
|
221
|
+
- [ ] `deadline` on every signature
|
|
222
|
+
- [ ] Signatures cannot be reused across different contract addresses
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## High: Logic Errors
|
|
227
|
+
|
|
228
|
+
**Checklist:**
|
|
229
|
+
- [ ] All invariants documented and tested (e.g., `totalSupply == sum of balances`)
|
|
230
|
+
- [ ] Edge cases: zero amounts, single user, max amounts, empty state
|
|
231
|
+
- [ ] Rounding direction favors the protocol, not the user (round down on user withdrawals)
|
|
232
|
+
- [ ] State transitions are complete and correct (no half-updated state on revert)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Pre-Deployment Checklist
|
|
237
|
+
|
|
238
|
+
### Code review
|
|
239
|
+
- [ ] Static analysis: `slither .` — zero critical/high findings
|
|
240
|
+
- [ ] Fuzzing: Foundry `forge fuzz` on all state-changing functions
|
|
241
|
+
- [ ] Invariant tests: Foundry invariant testing for protocol-level invariants
|
|
242
|
+
- [ ] Fork testing: mainnet fork with realistic protocols (DeFi integrations)
|
|
243
|
+
- [ ] Independent audit (3rd party, not affiliated team)
|
|
244
|
+
|
|
245
|
+
### Deployment
|
|
246
|
+
- [ ] Compiler version pinned: `pragma solidity 0.8.24;` (not `^`)
|
|
247
|
+
- [ ] All constructor arguments verified correct
|
|
248
|
+
- [ ] Contract verified on Etherscan before calling any admin function
|
|
249
|
+
- [ ] Multisig wallet as owner/admin (≥3 of 5)
|
|
250
|
+
- [ ] Timelock on all parameter changes (≥24h, ≥48h for critical)
|
|
251
|
+
- [ ] Deployment addresses, block numbers, and ABI hashes recorded
|
|
252
|
+
|
|
253
|
+
### Operations
|
|
254
|
+
- [ ] Emergency pause mechanism tested on testnet
|
|
255
|
+
- [ ] Incident response plan documented (who to call, how to pause, how to communicate)
|
|
256
|
+
- [ ] Monitoring: alerts on unusual value flows, failed transactions, admin calls
|
|
257
|
+
- [ ] Upgrade strategy defined (proxy pattern) or explicitly immutable with documentation
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Testing requirements by severity
|
|
262
|
+
|
|
263
|
+
| Scenario | Test type | Coverage |
|
|
264
|
+
|---|---|---|
|
|
265
|
+
| Normal operations | Unit tests | 100% |
|
|
266
|
+
| Access control violations | Unit tests | Every protected function |
|
|
267
|
+
| Reentrancy attacks | Fork tests with attacker contract | All external-call functions |
|
|
268
|
+
| Oracle manipulation | Fork tests with large swaps | All price-dependent paths |
|
|
269
|
+
| Integer edge cases | Fuzz tests | All arithmetic functions |
|
|
270
|
+
| Protocol invariants | Invariant tests | Total supply, balances, fees |
|
|
271
|
+
| Flash loan attacks | Fork tests with vm.deal | All single-tx exploit vectors |
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Emergency response
|
|
276
|
+
|
|
277
|
+
If a vulnerability is found post-deployment:
|
|
278
|
+
1. Pause the contract immediately (if pause mechanism exists)
|
|
279
|
+
2. Assess: is there active exploitation? How much at risk?
|
|
280
|
+
3. Do NOT disclose publicly until a fix is ready or funds are rescued
|
|
281
|
+
4. Contact affected protocols/integrations privately
|
|
282
|
+
5. Prepare and test the fix in isolation
|
|
283
|
+
6. Coordinate with security researchers / auditors
|
|
284
|
+
7. Deploy fix with a clear timeline communicated to users
|