@rev-net/core-v6 0.0.4 → 0.0.6
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/README.md +149 -29
- package/SKILLS.md +189 -101
- package/package.json +1 -1
- package/src/REVDeployer.sol +9 -4
- package/src/REVLoans.sol +5 -1
- package/test/REVAutoIssuanceFuzz.t.sol +5 -5
- package/test/REVDeployerAuditRegressions.t.sol +20 -20
- package/test/REVInvincibility.t.sol +48 -48
- package/test/REVLoans.invariants.t.sol +1 -1
- package/test/REVLoansAttacks.t.sol +11 -11
- package/test/REVLoansAuditRegressions.t.sol +12 -12
- package/test/REVLoansSourced.t.sol +1 -1
- package/test/TestEmptyBuybackSpecs.t.sol +237 -0
- package/test/TestPR12_FlashLoanSurplus.t.sol +1 -1
- package/test/TestPR16_ZeroRepayment.t.sol +1 -1
- package/test/TestPR21_Uint112Overflow.t.sol +1 -1
- package/test/TestPR22_HookArrayOOB.t.sol +1 -1
- package/test/TestPR27_CEIPattern.t.sol +2 -2
- package/test/TestStageTransitionBorrowable.t.sol +241 -0
- package/test/helpers/MaliciousContracts.sol +2 -2
- package/test/mock/MockBuybackDataHookMintPath.sol +61 -0
- package/REVNET_SECURITY_CHECKLIST.md +0 -164
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Revnet Pre-Deployment Security Checklist
|
|
2
|
-
|
|
3
|
-
**Status**: All findings documented, tests written. Fixes required before deployment.
|
|
4
|
-
**Test Suite**: `forge test --match-contract REVInvincibility -vvv`
|
|
5
|
-
**Date**: 2026-02-21
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Critical Findings (5)
|
|
10
|
-
|
|
11
|
-
### C-1: uint112 Truncation in REVLoans._adjust
|
|
12
|
-
- **File**: `REVLoans.sol:922-923`
|
|
13
|
-
- **Code**: `loan.amount = uint112(newBorrowAmount); loan.collateral = uint112(newCollateralCount);`
|
|
14
|
-
- **Impact**: Silently truncates borrow amounts > 5.19e33 (uint112.max), allowing loans with near-zero recorded debt but full ETH disbursement
|
|
15
|
-
- **Fix Required**: Add `require(newBorrowAmount <= type(uint112).max)` and `require(newCollateralCount <= type(uint112).max)` before the casts
|
|
16
|
-
- **Test**: `test_fixVerify_C1_uint112Truncation` — proves the truncation math
|
|
17
|
-
- **Status**: [ ] UNFIXED
|
|
18
|
-
|
|
19
|
-
### C-2: Array OOB in REVDeployer.beforePayRecordedWith
|
|
20
|
-
- **File**: `REVDeployer.sol:248-258`
|
|
21
|
-
- **Code**: `hookSpecifications[1] = buybackHookSpecifications[0]` hardcoded to index [1]
|
|
22
|
-
- **Impact**: Payment to any revnet with buyback hook but no 721 hook reverts (OOB on size-1 array)
|
|
23
|
-
- **Fix Required**: Change line 258 to use dynamic index: `hookSpecifications[usesTiered721Hook ? 1 : 0]`
|
|
24
|
-
- **Test**: `test_fixVerify_C2_arrayOOB_noBuybackWithBuyback` — proves the index logic
|
|
25
|
-
- **Status**: [ ] UNFIXED
|
|
26
|
-
|
|
27
|
-
### C-3: Reentrancy in REVLoans._adjust
|
|
28
|
-
- **File**: `REVLoans.sol:910 vs 922-923`
|
|
29
|
-
- **Code**: `terminal.pay()` at line 910 before `loan.amount = ...` at line 922
|
|
30
|
-
- **Impact**: Malicious fee terminal can reenter borrowFrom() with stale loan state, potentially extracting more than collateral supports
|
|
31
|
-
- **Fix Required**: Either add `nonReentrant` modifier or move state writes (lines 922-923) before external calls (line 910)
|
|
32
|
-
- **Test**: `test_fixVerify_C3_reentrancyDoubleBorrow` — confirms CEI violation pattern
|
|
33
|
-
- **Status**: [ ] UNFIXED
|
|
34
|
-
|
|
35
|
-
### C-4: hasMintPermissionFor Reverts on address(0)
|
|
36
|
-
- **File**: `REVDeployer.sol:353-354`
|
|
37
|
-
- **Code**: `buybackHook.hasMintPermissionFor(...)` called when `buybackHook == address(0)`
|
|
38
|
-
- **Impact**: Blocks ALL sucker claims and external mint operations for revnets without buyback hooks
|
|
39
|
-
- **Fix Required**: Add `address(buybackHook) != address(0) &&` guard before the call
|
|
40
|
-
- **Test**: `test_fixVerify_C4_hasMintPermission_noBuyback` — triggers the revert
|
|
41
|
-
- **Status**: [ ] UNFIXED
|
|
42
|
-
|
|
43
|
-
### C-5: Zero-Supply Cash Out Drains Surplus
|
|
44
|
-
- **File**: `JBCashOuts.sol:31`
|
|
45
|
-
- **Code**: `if (cashOutCount >= totalSupply) return surplus;` — 0 >= 0 is true
|
|
46
|
-
- **Impact**: When totalSupply=0, cashing out 0 tokens returns the ENTIRE surplus
|
|
47
|
-
- **Fix Required**: Add `if (cashOutCount == 0) return 0;` before the totalSupply check
|
|
48
|
-
- **Test**: `test_fixVerify_C5_zeroSupplyCashOutDrain` — proves 0/0 returns full surplus
|
|
49
|
-
- **Status**: [ ] UNFIXED (in JBCashOuts library)
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## High Findings (4 revnet-specific)
|
|
54
|
-
|
|
55
|
-
### H-1: Double Fee on Cash-Outs
|
|
56
|
-
- **File**: `REVDeployer.sol:567-624`
|
|
57
|
-
- **Impact**: Cash-out fees are charged twice — once by JBMultiTerminal (protocol fee) and once by REVDeployer's afterCashOutRecordedWith (revnet fee). REVDeployer is not registered as feeless.
|
|
58
|
-
- **Fix Required**: Register REVDeployer as feeless address, or adjust fee calculation to account for the protocol fee already taken
|
|
59
|
-
- **Test**: `test_econ_doubleFeeH1` — measures actual fee amounts
|
|
60
|
-
- **Status**: [ ] UNFIXED
|
|
61
|
-
|
|
62
|
-
### H-2: Broken Fee Terminal Bricks Cash-Outs
|
|
63
|
-
- **File**: `REVDeployer.sol:615`
|
|
64
|
-
- **Impact**: In the catch block of afterCashOutRecordedWith, `addToBalanceOf()` is NOT wrapped in try/catch. If both feeTerminal.pay() and addToBalanceOf() revert, ALL cash-outs for the revnet become permanently impossible.
|
|
65
|
-
- **Fix Required**: Wrap the fallback `addToBalanceOf()` at line 615 in its own try/catch
|
|
66
|
-
- **Test**: `test_fixVerify_H2_brokenFeeTerminalBricksCashOuts` — demonstrates both paths revert
|
|
67
|
-
- **Status**: [ ] UNFIXED
|
|
68
|
-
|
|
69
|
-
### H-5: Auto-Issuance Stage ID Mismatch
|
|
70
|
-
- **File**: `REVDeployer.sol:1223`
|
|
71
|
-
- **Code**: `amountToAutoIssue[revnetId][block.timestamp + i][...] += ...`
|
|
72
|
-
- **Impact**: Stage ID computed as `block.timestamp + i` but actual ruleset IDs from JBRulesets may differ. Auto-issuance tokens for non-first stages become permanently unclaimable.
|
|
73
|
-
- **Fix Required**: Use the actual ruleset IDs returned by `jbController().queueRulesetsOf()` instead of `block.timestamp + i`
|
|
74
|
-
- **Test**: `test_fixVerify_H5_autoIssuanceStageIdMismatch` — confirms mismatch for stage 1+
|
|
75
|
-
- **Status**: [ ] UNFIXED
|
|
76
|
-
|
|
77
|
-
### H-6: Unvalidated Source Terminal
|
|
78
|
-
- **File**: `REVLoans.sol:788-791`
|
|
79
|
-
- **Impact**: Any terminal can be registered as a loan source. Attacker can grow `_loanSourcesOf` array unboundedly, causing gas DoS on functions that iterate loan sources.
|
|
80
|
-
- **Fix Required**: Validate that `loan.source.terminal` is a registered terminal for the project via `DIRECTORY.isTerminalOf(revnetId, loan.source.terminal)`
|
|
81
|
-
- **Test**: `test_fixVerify_H6_unvalidatedSourceTerminal` — documents the unvalidated registration
|
|
82
|
-
- **Status**: [ ] UNFIXED
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Medium Findings (3 revnet-specific)
|
|
87
|
-
|
|
88
|
-
### M-7: Silent Fee Failure in REVLoans._addTo
|
|
89
|
-
- **File**: `REVLoans.sol:833-841`
|
|
90
|
-
- **Impact**: REV fee payment in `_addTo` is wrapped in try/catch. If the fee terminal reverts, the fee is silently lost — REV holders lose fee revenue without any notification.
|
|
91
|
-
- **Fix Required**: At minimum, emit an event on fee failure. Consider reverting to ensure fees are always collected.
|
|
92
|
-
- **Status**: [ ] UNFIXED
|
|
93
|
-
|
|
94
|
-
### M-10: Cross-Source Value Extraction via reallocateCollateralFromLoan
|
|
95
|
-
- **File**: `REVLoans.sol:619-654`
|
|
96
|
-
- **Impact**: Collateral from one loan source can be transferred to create a loan from a different source. If source terminals have different fee structures, this enables fee arbitrage.
|
|
97
|
-
- **Fix Required**: Consider restricting collateral reallocation to same-source loans
|
|
98
|
-
- **Status**: [ ] UNFIXED
|
|
99
|
-
|
|
100
|
-
### M-11: Flash Loan Surplus Inflation
|
|
101
|
-
- **File**: `REVLoans.sol:308-332`
|
|
102
|
-
- **Impact**: `borrowableAmountFrom` reads live surplus. An attacker can `addToBalance` (inflating surplus without minting tokens) then immediately borrow at an inflated rate within the same block.
|
|
103
|
-
- **Fix Required**: Consider using a time-weighted average surplus or adding a borrowing delay
|
|
104
|
-
- **Test**: `test_econ_flashLoanSurplusInflation` — quantifies exact inflation factor
|
|
105
|
-
- **Status**: [ ] UNFIXED
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
## Invariant Properties (Verified by Fuzzing)
|
|
110
|
-
|
|
111
|
-
| ID | Property | Handler Operations | Runs |
|
|
112
|
-
|----|----------|-------------------|------|
|
|
113
|
-
| INV-REV-1 | Terminal balance covers outstanding loans | payAndBorrow, repayLoan, addToBalance | 256 |
|
|
114
|
-
| INV-REV-2 | Ghost collateral sum == totalCollateralOf | payAndBorrow, repayLoan, reallocate | 256 |
|
|
115
|
-
| INV-REV-3 | Ghost borrowed sum == totalBorrowedFrom | payAndBorrow, repayLoan | 256 |
|
|
116
|
-
| INV-REV-4 | No undercollateralized loans (when no cash-outs) | payAndBorrow, advanceTime | 256 |
|
|
117
|
-
| INV-REV-5 | totalSupply + totalCollateral coherent | All 10 operations | 256 |
|
|
118
|
-
| INV-REV-6 | Fee project balance monotonically increasing | payAndBorrow (generates fees) | 256 |
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Test Execution
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# Section A+B: Fix verification + economic attacks (18 tests)
|
|
126
|
-
forge test --match-contract REVInvincibility_FixVerify -vvv
|
|
127
|
-
|
|
128
|
-
# Section C: Invariant properties (6 invariants)
|
|
129
|
-
forge test --match-contract REVInvincibility_Invariants -vvv
|
|
130
|
-
|
|
131
|
-
# Full suite
|
|
132
|
-
forge test --match-contract REVInvincibility -vvv
|
|
133
|
-
|
|
134
|
-
# Full regression (all existing tests still pass)
|
|
135
|
-
forge test -vvv
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Post-Deployment Monitoring Recommendations
|
|
141
|
-
|
|
142
|
-
1. **Loan Health Monitor**: Track `totalBorrowedFrom` vs terminal balance for each revnet. Alert if borrowed exceeds 80% of surplus.
|
|
143
|
-
2. **Fee Collection Monitor**: Verify fee project token supply increases after every borrow operation. Alert on silent fee failures.
|
|
144
|
-
3. **Collateral Consistency**: Periodically verify `sum(loan.collateral)` for all active loans matches `totalCollateralOf(revnetId)`.
|
|
145
|
-
4. **Loan Source Array**: Monitor `_loanSourcesOf` array length. Alert if it exceeds expected number of terminals.
|
|
146
|
-
5. **Auto-Issuance Claims**: After each stage transition, verify auto-issuance can be claimed at the correct ruleset ID.
|
|
147
|
-
6. **Cash-Out Availability**: Monitor that cash-outs succeed after fee terminal configuration changes.
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## Fix Priority Order
|
|
152
|
-
|
|
153
|
-
1. **C-3** (Reentrancy) — Highest risk, enables active exploitation
|
|
154
|
-
2. **C-5** (Zero-supply drain) — Direct fund loss
|
|
155
|
-
3. **C-1** (uint112 truncation) — Fund loss at extreme values
|
|
156
|
-
4. **C-4** (hasMintPermission revert) — Blocks sucker claims
|
|
157
|
-
5. **C-2** (Array OOB) — Breaks payments for buyback-only revnets
|
|
158
|
-
6. **H-2** (Broken fee terminal) — Permanent cash-out DoS
|
|
159
|
-
7. **H-5** (Auto-issuance mismatch) — Permanent token loss
|
|
160
|
-
8. **H-6** (Unvalidated terminal) — Gas DoS vector
|
|
161
|
-
9. **H-1** (Double fee) — Economic loss for users
|
|
162
|
-
10. **M-11** (Flash surplus inflation) — Economic exploitation
|
|
163
|
-
11. **M-10** (Cross-source extraction) — Fee arbitrage
|
|
164
|
-
12. **M-7** (Silent fee failure) — Revenue leakage
|