solidity-argus 0.1.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.
Files changed (131) hide show
  1. package/AGENTS.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +249 -0
  4. package/package.json +43 -0
  5. package/skills/INVENTORY.md +79 -0
  6. package/skills/README.md +56 -0
  7. package/skills/checklists/cyfrin-best-practices-runtime/SKILL.md +424 -0
  8. package/skills/checklists/cyfrin-best-practices-upgrades/SKILL.md +157 -0
  9. package/skills/checklists/cyfrin-defi-core/SKILL.md +373 -0
  10. package/skills/checklists/cyfrin-defi-integrations/SKILL.md +412 -0
  11. package/skills/checklists/cyfrin-gas/SKILL.md +55 -0
  12. package/skills/checklists/general-audit/SKILL.md +433 -0
  13. package/skills/methodology/audit-workflow/SKILL.md +129 -0
  14. package/skills/methodology/report-template/SKILL.md +190 -0
  15. package/skills/methodology/severity-classification/SKILL.md +179 -0
  16. package/skills/protocol-patterns/amm-dex/SKILL.md +229 -0
  17. package/skills/protocol-patterns/bridges-cross-chain/SKILL.md +317 -0
  18. package/skills/protocol-patterns/dao-governance/SKILL.md +281 -0
  19. package/skills/protocol-patterns/lending-borrowing/SKILL.md +221 -0
  20. package/skills/protocol-patterns/staking-vesting/SKILL.md +247 -0
  21. package/skills/references/exploit-reference/SKILL.md +259 -0
  22. package/skills/references/smartbugs-examples/SKILL.md +296 -0
  23. package/skills/vulnerability-patterns/access-control/SKILL.md +298 -0
  24. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +59 -0
  25. package/skills/vulnerability-patterns/assert-violation/SKILL.md +59 -0
  26. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +61 -0
  27. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +55 -0
  28. package/skills/vulnerability-patterns/default-visibility/SKILL.md +62 -0
  29. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +60 -0
  30. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +59 -0
  31. package/skills/vulnerability-patterns/dos-revert/SKILL.md +72 -0
  32. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +249 -0
  33. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +51 -0
  34. package/skills/vulnerability-patterns/hash-collision/SKILL.md +52 -0
  35. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +61 -0
  36. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +60 -0
  37. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +59 -0
  38. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +61 -0
  39. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +61 -0
  40. package/skills/vulnerability-patterns/logic-errors/SKILL.md +333 -0
  41. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +60 -0
  42. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +66 -0
  43. package/skills/vulnerability-patterns/off-by-one/SKILL.md +67 -0
  44. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +252 -0
  45. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +65 -0
  46. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +61 -0
  47. package/skills/vulnerability-patterns/reentrancy/SKILL.md +266 -0
  48. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +72 -0
  49. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +59 -0
  50. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +63 -0
  51. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +52 -0
  52. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +65 -0
  53. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +61 -0
  54. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +63 -0
  55. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +56 -0
  56. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +80 -0
  57. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +69 -0
  58. package/skills/vulnerability-patterns/unused-variables/SKILL.md +70 -0
  59. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +81 -0
  60. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +77 -0
  61. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +294 -0
  62. package/src/agents/argus-prompt.ts +407 -0
  63. package/src/agents/pythia-prompt.ts +134 -0
  64. package/src/agents/scribe-prompt.ts +87 -0
  65. package/src/agents/sentinel-prompt.ts +133 -0
  66. package/src/cli/cli-program.ts +67 -0
  67. package/src/cli/commands/doctor.ts +83 -0
  68. package/src/cli/commands/init.ts +46 -0
  69. package/src/cli/commands/install.ts +55 -0
  70. package/src/cli/index.ts +13 -0
  71. package/src/cli/tui-prompts.ts +75 -0
  72. package/src/cli/types.ts +9 -0
  73. package/src/config/index.ts +3 -0
  74. package/src/config/loader.ts +36 -0
  75. package/src/config/schema.ts +82 -0
  76. package/src/config/types.ts +4 -0
  77. package/src/constants/defaults.ts +6 -0
  78. package/src/create-hooks.ts +84 -0
  79. package/src/create-managers.ts +26 -0
  80. package/src/create-tools.ts +30 -0
  81. package/src/features/audit-enforcer/audit-enforcer.ts +34 -0
  82. package/src/features/audit-enforcer/index.ts +1 -0
  83. package/src/features/background-agent/background-manager.ts +200 -0
  84. package/src/features/background-agent/index.ts +1 -0
  85. package/src/features/context-monitor/context-monitor.ts +48 -0
  86. package/src/features/context-monitor/index.ts +4 -0
  87. package/src/features/context-monitor/tool-output-truncator.ts +17 -0
  88. package/src/features/error-recovery/index.ts +2 -0
  89. package/src/features/error-recovery/session-recovery.ts +27 -0
  90. package/src/features/error-recovery/tool-error-recovery.ts +35 -0
  91. package/src/features/index.ts +5 -0
  92. package/src/features/persistent-state/audit-state-manager.ts +121 -0
  93. package/src/features/persistent-state/index.ts +1 -0
  94. package/src/hooks/compaction-hook.ts +50 -0
  95. package/src/hooks/config-handler.ts +116 -0
  96. package/src/hooks/event-hook-v2.ts +93 -0
  97. package/src/hooks/event-hook.ts +74 -0
  98. package/src/hooks/hook-system.ts +9 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/knowledge-sync-hook.ts +57 -0
  101. package/src/hooks/safe-create-hook.ts +15 -0
  102. package/src/hooks/system-prompt-hook.ts +126 -0
  103. package/src/hooks/tool-tracking-hook.ts +234 -0
  104. package/src/hooks/types.ts +16 -0
  105. package/src/index.ts +36 -0
  106. package/src/knowledge/scvd-client.ts +242 -0
  107. package/src/knowledge/scvd-index.ts +183 -0
  108. package/src/knowledge/scvd-sync.ts +85 -0
  109. package/src/managers/index.ts +1 -0
  110. package/src/managers/types.ts +85 -0
  111. package/src/plugin-interface.ts +38 -0
  112. package/src/shared/binary-utils.ts +63 -0
  113. package/src/shared/deep-merge.ts +71 -0
  114. package/src/shared/file-utils.ts +56 -0
  115. package/src/shared/index.ts +5 -0
  116. package/src/shared/jsonc-parser.ts +39 -0
  117. package/src/shared/logger.ts +36 -0
  118. package/src/state/audit-state.ts +27 -0
  119. package/src/state/finding-store.ts +126 -0
  120. package/src/state/plugin-state.ts +14 -0
  121. package/src/state/types.ts +61 -0
  122. package/src/tools/contract-analyzer-tool.ts +184 -0
  123. package/src/tools/forge-fuzz-tool.ts +311 -0
  124. package/src/tools/forge-test-tool.ts +397 -0
  125. package/src/tools/pattern-checker-tool.ts +337 -0
  126. package/src/tools/report-generator-tool.ts +308 -0
  127. package/src/tools/slither-tool.ts +465 -0
  128. package/src/tools/solodit-search-tool.ts +131 -0
  129. package/src/tools/sync-knowledge-tool.ts +116 -0
  130. package/src/utils/project-detector.ts +133 -0
  131. package/src/utils/solidity-parser.ts +174 -0
@@ -0,0 +1,221 @@
1
+ ---
2
+ name: lending-borrowing
3
+ description: Security review framework for lending and borrowing systems including liquidations and accounting.
4
+ ---
5
+ <!-- Source: DeFiFoFum/fofum-solidity-skills (MIT) -->
6
+
7
+ # Lending Protocol Security Guide
8
+
9
+ ## Overview
10
+
11
+ Lending protocols (Aave, Compound, Morpho) enable collateralized borrowing. Core security concerns: liquidation logic, interest accrual, oracle reliance, and collateral management.
12
+
13
+ ---
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ ┌─────────────────────────────────────────────────────────────┐
19
+ │ LENDING PROTOCOL │
20
+ ├─────────────────────────────────────────────────────────────┤
21
+ │ SUPPLY │ BORROW │ LIQUIDATE │
22
+ │ ───────── │ ───────── │ ───────── │
23
+ │ Deposit asset │ Lock collateral │ Seize collateral │
24
+ │ Receive shares │ Receive asset │ Repay debt │
25
+ │ Earn interest │ Pay interest │ Get bonus │
26
+ ├─────────────────────────────────────────────────────────────┤
27
+ │ RISK ENGINE │
28
+ │ Health Factor = Collateral Value / Borrowed Value │
29
+ │ If HF < 1: Position is liquidatable │
30
+ └─────────────────────────────────────────────────────────────┘
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Critical Security Areas
36
+
37
+ ### 1. Liquidation Logic
38
+
39
+ **Attack Vectors:**
40
+ - Self-liquidation for profit
41
+ - Liquidation front-running
42
+ - Incorrect health factor calculation
43
+ - Liquidation bonus manipulation
44
+
45
+ **Checklist:**
46
+ - [ ] Can users self-liquidate profitably?
47
+ - [ ] Is health factor calculation correct with all decimal handling?
48
+ - [ ] Is liquidation bonus reasonable (not exploitable)?
49
+ - [ ] Are partial liquidations handled correctly?
50
+ - [ ] Can dust amounts block liquidation?
51
+
52
+ ```solidity
53
+ // VULNERABLE: No self-liquidation check
54
+ function liquidate(address borrower, uint256 amount) external {
55
+ require(getHealthFactor(borrower) < 1e18, "Healthy");
56
+ // Missing: require(msg.sender != borrower, "No self-liquidation");
57
+ }
58
+ ```
59
+
60
+ ### 2. Interest Rate Model
61
+
62
+ **Attack Vectors:**
63
+ - Interest rate manipulation via large deposits/borrows
64
+ - Accrual timing exploits
65
+ - Compound interest calculation errors
66
+
67
+ **Checklist:**
68
+ - [ ] Is interest accrued before all operations?
69
+ - [ ] Are utilization rate calculations correct?
70
+ - [ ] Can interest rate be manipulated within a transaction?
71
+ - [ ] Are there bounds on interest rates?
72
+
73
+ ```solidity
74
+ // VULNERABLE: Interest not accrued before operation
75
+ function withdraw(uint256 amount) external {
76
+ // Missing: accrueInterest();
77
+ uint256 shares = amount * totalShares / totalAssets;
78
+ _burn(msg.sender, shares);
79
+ }
80
+
81
+ // SECURE
82
+ function withdraw(uint256 amount) external {
83
+ accrueInterest(); // Always accrue first
84
+ uint256 shares = amount * totalShares / totalAssets;
85
+ _burn(msg.sender, shares);
86
+ }
87
+ ```
88
+
89
+ ### 3. Oracle Dependency
90
+
91
+ **Attack Vectors:**
92
+ - Price oracle manipulation
93
+ - Stale price exploitation
94
+ - Flash loan + oracle attack
95
+
96
+ **Checklist:**
97
+ - [ ] Are oracle prices validated for freshness?
98
+ - [ ] Are price bounds checked?
99
+ - [ ] Can prices be manipulated via flash loans?
100
+ - [ ] Is there circuit breaker for price anomalies?
101
+
102
+ See: [oracle.md](./exploits/oracle.md)
103
+
104
+ ### 4. Collateral Management
105
+
106
+ **Attack Vectors:**
107
+ - Depositing worthless collateral
108
+ - Collateral factor manipulation
109
+ - Bad debt accumulation
110
+
111
+ **Checklist:**
112
+ - [ ] Are collateral factors appropriate for asset volatility?
113
+ - [ ] Is there a whitelist for supported collateral?
114
+ - [ ] How is bad debt handled?
115
+ - [ ] Can users withdraw collateral below safe threshold?
116
+
117
+ ### 5. Share/Asset Accounting (ERC4626)
118
+
119
+ **Attack Vectors:**
120
+ - Inflation attacks on first deposit
121
+ - Rounding direction exploitation
122
+ - Donation attacks
123
+
124
+ **Checklist:**
125
+ - [ ] Is first depositor protected from inflation attack?
126
+ - [ ] Does rounding favor the protocol (round down on mint, up on burn)?
127
+ - [ ] Are direct asset transfers (donations) handled?
128
+ - [ ] Is totalAssets always ≥ sum of deposits?
129
+
130
+ ```solidity
131
+ // Inflation attack protection
132
+ function deposit(uint256 assets) external returns (uint256 shares) {
133
+ require(assets >= MINIMUM_DEPOSIT, "Below minimum");
134
+ shares = totalSupply == 0
135
+ ? assets // First deposit
136
+ : assets * totalSupply / totalAssets;
137
+ require(shares > 0, "Zero shares");
138
+ // ...
139
+ }
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Common Vulnerabilities
145
+
146
+ ### Reentrancy in Lending
147
+
148
+ ```solidity
149
+ // VULNERABLE: CEI violation
150
+ function borrow(uint256 amount) external {
151
+ require(checkCollateral(msg.sender, amount), "Undercollateralized");
152
+ token.transfer(msg.sender, amount); // External call before state update
153
+ borrowBalances[msg.sender] += amount; // State update after
154
+ }
155
+ ```
156
+
157
+ ### Incorrect Decimal Handling
158
+
159
+ ```solidity
160
+ // VULNERABLE: Assumes all tokens have 18 decimals
161
+ function calculateValue(address token, uint256 amount) public view returns (uint256) {
162
+ uint256 price = oracle.getPrice(token); // Price in USD with 8 decimals
163
+ return amount * price / 1e8; // Wrong if token isn't 18 decimals!
164
+ }
165
+
166
+ // SECURE
167
+ function calculateValue(address token, uint256 amount) public view returns (uint256) {
168
+ uint256 price = oracle.getPrice(token);
169
+ uint8 decimals = IERC20Metadata(token).decimals();
170
+ return amount * price / (10 ** decimals);
171
+ }
172
+ ```
173
+
174
+ ### Liquidation Threshold Edge Cases
175
+
176
+ ```solidity
177
+ // VULNERABLE: Dust prevents liquidation
178
+ function liquidate(address borrower, uint256 amount) external {
179
+ uint256 debt = borrowBalances[borrower];
180
+ require(amount <= debt, "Too much");
181
+ // If debt = 100 wei and minimum repay = 100 wei, can't liquidate
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Testing Checklist
188
+
189
+ ### Unit Tests
190
+ - [ ] Deposit/withdraw accounting correct
191
+ - [ ] Borrow/repay accounting correct
192
+ - [ ] Interest accrual over time
193
+ - [ ] Liquidation triggers at correct threshold
194
+ - [ ] Health factor calculation
195
+
196
+ ### Integration Tests
197
+ - [ ] Oracle integration
198
+ - [ ] Multi-asset interactions
199
+ - [ ] Liquidation bot behavior
200
+
201
+ ### Invariant Tests
202
+ - [ ] totalBorrowed ≤ totalSupplied * maxUtilization
203
+ - [ ] Each user's healthFactor > 1 OR liquidatable
204
+ - [ ] Sum of deposits = totalAssets (accounting)
205
+ - [ ] No negative balances
206
+
207
+ ### Edge Case Tests
208
+ - [ ] First deposit (empty pool)
209
+ - [ ] Last withdrawal (drain pool)
210
+ - [ ] Zero amount operations
211
+ - [ ] Max uint256 operations
212
+ - [ ] 1 wei operations
213
+
214
+ ---
215
+
216
+ ## References
217
+
218
+ - [Aave V3 Security](https://github.com/aave/aave-v3-core/tree/master/audits)
219
+ - [Compound Security Considerations](https://docs.compound.finance/security/)
220
+ - [ERC4626 Security Considerations](https://eips.ethereum.org/EIPS/eip-4626#security-considerations)
221
+ - [Morpho Security](https://docs.morpho.org/morpho-blue/security-and-risk-management)
@@ -0,0 +1,247 @@
1
+ ---
2
+ name: staking-vesting
3
+ description: Staking security guidance for reward accounting, lock periods, timing attacks, and withdrawals.
4
+ ---
5
+ <!-- Source: DeFiFoFum/fofum-solidity-skills (MIT) -->
6
+
7
+ # Staking Protocol Security Guide
8
+
9
+ ## Overview
10
+
11
+ Staking protocols lock tokens to earn rewards. Core security concerns: reward calculation, timing attacks, withdrawal delays, and token accounting.
12
+
13
+ ---
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ ┌─────────────────────────────────────────────────────────────┐
19
+ │ STAKING PROTOCOL │
20
+ ├─────────────────────────────────────────────────────────────┤
21
+ │ │
22
+ │ STAKE EARN UNSTAKE │
23
+ │ ────── ──── ─────── │
24
+ │ Lock tokens → Accrue rewards → Withdraw + rewards │
25
+ │ │
26
+ │ rewardPerToken = totalRewards / totalStaked / time │
27
+ │ │
28
+ │ userReward = (rewardPerToken - userRewardPaid) * balance │
29
+ │ │
30
+ └─────────────────────────────────────────────────────────────┘
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Critical Security Areas
36
+
37
+ ### 1. Reward Calculation
38
+
39
+ **Attack Vectors:**
40
+ - Claim rewards multiple times
41
+ - Manipulation of rewardPerToken
42
+ - Precision loss in reward math
43
+
44
+ **Checklist:**
45
+ - [ ] Is rewardPerTokenStored updated before any balance changes?
46
+ - [ ] Is userRewardPerTokenPaid updated when claiming?
47
+ - [ ] Can rewards be claimed multiple times for same period?
48
+ - [ ] Is precision sufficient (multiply before divide)?
49
+
50
+ ```solidity
51
+ // Standard Synthetix staking pattern
52
+ uint256 public rewardPerTokenStored;
53
+ mapping(address => uint256) public userRewardPerTokenPaid;
54
+ mapping(address => uint256) public rewards;
55
+
56
+ modifier updateReward(address account) {
57
+ rewardPerTokenStored = rewardPerToken();
58
+ lastUpdateTime = block.timestamp;
59
+ if (account != address(0)) {
60
+ rewards[account] = earned(account);
61
+ userRewardPerTokenPaid[account] = rewardPerTokenStored;
62
+ }
63
+ _;
64
+ }
65
+
66
+ function earned(address account) public view returns (uint256) {
67
+ return balances[account] *
68
+ (rewardPerToken() - userRewardPerTokenPaid[account]) / 1e18 +
69
+ rewards[account];
70
+ }
71
+ ```
72
+
73
+ ### 2. Deposit/Withdrawal Timing
74
+
75
+ **Attack Vectors:**
76
+ - Stake just before rewards, unstake right after
77
+ - Flash loan staking
78
+ - MEV on reward distribution
79
+
80
+ **Checklist:**
81
+ - [ ] Is there a minimum stake duration?
82
+ - [ ] Are rewards distributed over time (not lump sum)?
83
+ - [ ] Can someone stake in same block as reward distribution?
84
+ - [ ] Is there unstaking delay/cooldown?
85
+
86
+ ```solidity
87
+ // VULNERABLE: Instant stake and claim
88
+ function distributeReward(uint256 amount) external {
89
+ rewardToken.transferFrom(msg.sender, address(this), amount);
90
+ rewardPerTokenStored += amount * 1e18 / totalStaked;
91
+ // Flash staker can stake before this, claim after
92
+ }
93
+
94
+ // SECURE: Drip rewards over time
95
+ function notifyRewardAmount(uint256 reward) external {
96
+ if (block.timestamp >= periodFinish) {
97
+ rewardRate = reward / DURATION;
98
+ } else {
99
+ uint256 remaining = periodFinish - block.timestamp;
100
+ uint256 leftover = remaining * rewardRate;
101
+ rewardRate = (reward + leftover) / DURATION;
102
+ }
103
+ lastUpdateTime = block.timestamp;
104
+ periodFinish = block.timestamp + DURATION;
105
+ }
106
+ ```
107
+
108
+ ### 3. Token Accounting
109
+
110
+ **Attack Vectors:**
111
+ - Donation attacks (direct transfer)
112
+ - Rebase token issues
113
+ - Fee-on-transfer tokens
114
+
115
+ **Checklist:**
116
+ - [ ] Are direct token transfers handled?
117
+ - [ ] Is actual received amount checked (fee-on-transfer)?
118
+ - [ ] Are rebase tokens supported? If so, how?
119
+ - [ ] Is totalStaked always equal to sum of balances?
120
+
121
+ ```solidity
122
+ // VULNERABLE: Assumes full amount received
123
+ function stake(uint256 amount) external {
124
+ stakingToken.transferFrom(msg.sender, address(this), amount);
125
+ balances[msg.sender] += amount; // Wrong for fee-on-transfer!
126
+ totalStaked += amount;
127
+ }
128
+
129
+ // SECURE: Check actual amount received
130
+ function stake(uint256 amount) external {
131
+ uint256 balanceBefore = stakingToken.balanceOf(address(this));
132
+ stakingToken.transferFrom(msg.sender, address(this), amount);
133
+ uint256 received = stakingToken.balanceOf(address(this)) - balanceBefore;
134
+
135
+ balances[msg.sender] += received;
136
+ totalStaked += received;
137
+ }
138
+ ```
139
+
140
+ ### 4. Lock/Unlock Mechanisms
141
+
142
+ **Attack Vectors:**
143
+ - Bypassing lock periods
144
+ - Lock period manipulation
145
+ - Emergency withdrawal exploits
146
+
147
+ **Checklist:**
148
+ - [ ] Is lock timestamp immutable after staking?
149
+ - [ ] Can partial unlocks bypass full lock?
150
+ - [ ] Is emergency withdrawal penalized appropriately?
151
+ - [ ] Are there reentrancy risks in unlock?
152
+
153
+ ```solidity
154
+ // VULNERABLE: Lock can be extended/reset incorrectly
155
+ function stake(uint256 amount) external {
156
+ balances[msg.sender] += amount;
157
+ lockEnd[msg.sender] = block.timestamp + LOCK_PERIOD; // Resets lock!
158
+ }
159
+
160
+ // SECURE: Don't reset existing locks
161
+ function stake(uint256 amount) external {
162
+ balances[msg.sender] += amount;
163
+ if (lockEnd[msg.sender] < block.timestamp) {
164
+ lockEnd[msg.sender] = block.timestamp + LOCK_PERIOD;
165
+ }
166
+ // Existing lock preserved
167
+ }
168
+ ```
169
+
170
+ ### 5. Reward Token Handling
171
+
172
+ **Attack Vectors:**
173
+ - Same token for stake and reward (inflation)
174
+ - Reward token donation manipulation
175
+ - Insufficient reward balance
176
+
177
+ **Checklist:**
178
+ - [ ] Is stake token different from reward token?
179
+ - [ ] Is there sufficient reward balance for all claims?
180
+ - [ ] Can reward distribution be griefed?
181
+ - [ ] Are multiple reward tokens handled correctly?
182
+
183
+ ---
184
+
185
+ ## Common Vulnerabilities
186
+
187
+ ### Reentrancy in Claim
188
+
189
+ ```solidity
190
+ // VULNERABLE: External call before state update
191
+ function claim() external {
192
+ uint256 reward = earned(msg.sender);
193
+ rewardToken.transfer(msg.sender, reward); // External call first
194
+ rewards[msg.sender] = 0; // State update after
195
+ }
196
+ ```
197
+
198
+ ### Division Before Multiplication
199
+
200
+ ```solidity
201
+ // VULNERABLE: Precision loss
202
+ function rewardPerToken() public view returns (uint256) {
203
+ if (totalStaked == 0) return rewardPerTokenStored;
204
+ return rewardPerTokenStored +
205
+ (rewardRate / totalStaked) * (block.timestamp - lastUpdate); // Wrong order!
206
+ }
207
+
208
+ // SECURE
209
+ function rewardPerToken() public view returns (uint256) {
210
+ if (totalStaked == 0) return rewardPerTokenStored;
211
+ return rewardPerTokenStored +
212
+ rewardRate * (block.timestamp - lastUpdate) * 1e18 / totalStaked;
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Testing Checklist
219
+
220
+ ### Unit Tests
221
+ - [ ] Stake/unstake accounting
222
+ - [ ] Reward accrual over time
223
+ - [ ] Multiple stakers, proportional rewards
224
+ - [ ] Claim resets user state correctly
225
+
226
+ ### Integration Tests
227
+ - [ ] Multiple reward periods
228
+ - [ ] Stakers joining mid-period
229
+ - [ ] Edge cases (first staker, last staker)
230
+
231
+ ### Invariant Tests
232
+ - [ ] totalStaked = sum(balances)
233
+ - [ ] Rewards claimable ≤ reward balance
234
+ - [ ] No user can claim more than entitled
235
+
236
+ ### Attack Tests
237
+ - [ ] Flash stake attack
238
+ - [ ] Double claim attempt
239
+ - [ ] Donation manipulation
240
+
241
+ ---
242
+
243
+ ## References
244
+
245
+ - [Synthetix Staking Rewards](https://github.com/Synthetixio/synthetix/blob/develop/contracts/StakingRewards.sol)
246
+ - [Convex Staking](https://github.com/convex-eth/platform)
247
+ - [MasterChef (SushiSwap)](https://github.com/sushiswap/sushiswap/blob/master/contracts/MasterChef.sol)