@rev-net/core-v6 0.0.7 → 0.0.9
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/ADMINISTRATION.md +186 -0
- package/ARCHITECTURE.md +87 -0
- package/README.md +4 -2
- package/RISKS.md +49 -0
- package/SKILLS.md +22 -2
- package/STYLE_GUIDE.md +482 -0
- package/foundry.toml +6 -6
- package/package.json +13 -10
- package/script/Deploy.s.sol +3 -2
- package/src/REVDeployer.sol +129 -72
- package/src/REVLoans.sol +174 -165
- package/src/interfaces/IREVDeployer.sol +111 -72
- package/src/interfaces/IREVLoans.sol +116 -76
- package/src/structs/REV721TiersHookFlags.sol +14 -0
- package/src/structs/REVBaseline721HookConfig.sol +27 -0
- package/src/structs/REVDeploy721TiersHookConfig.sol +2 -2
- package/test/REV.integrations.t.sol +4 -3
- package/test/REVAutoIssuanceFuzz.t.sol +12 -8
- package/test/REVDeployerAuditRegressions.t.sol +4 -3
- package/test/REVInvincibility.t.sol +8 -6
- package/test/REVInvincibilityHandler.sol +1 -0
- package/test/REVLifecycle.t.sol +4 -3
- package/test/REVLoans.invariants.t.sol +5 -3
- package/test/REVLoansAttacks.t.sol +4 -3
- package/test/REVLoansAuditRegressions.t.sol +13 -24
- package/test/REVLoansFeeRecovery.t.sol +4 -3
- package/test/REVLoansSourced.t.sol +4 -3
- package/test/REVLoansUnSourced.t.sol +4 -3
- package/test/REVLoans_AuditFindings.t.sol +644 -0
- package/test/TestEmptyBuybackSpecs.t.sol +4 -3
- package/test/TestPR09_ConversionDocumentation.t.sol +4 -3
- package/test/TestPR10_LiquidationBehavior.t.sol +4 -3
- package/test/TestPR11_LowFindings.t.sol +4 -3
- package/test/TestPR12_FlashLoanSurplus.t.sol +4 -3
- package/test/TestPR13_CrossSourceReallocation.t.sol +4 -3
- package/test/TestPR15_CashOutCallerValidation.t.sol +4 -3
- package/test/TestPR16_ZeroRepayment.t.sol +4 -3
- package/test/TestPR21_Uint112Overflow.t.sol +4 -3
- package/test/TestPR22_HookArrayOOB.t.sol +4 -3
- package/test/TestPR26_BurnHeldTokens.t.sol +4 -3
- package/test/TestPR27_CEIPattern.t.sol +4 -3
- package/test/TestPR29_SwapTerminalPermission.t.sol +4 -3
- package/test/TestPR32_MixedFixes.t.sol +4 -3
- package/test/TestSplitWeightAdjustment.t.sol +445 -0
- package/test/TestSplitWeightE2E.t.sol +528 -0
- package/test/TestSplitWeightFork.t.sol +821 -0
- package/test/TestStageTransitionBorrowable.t.sol +4 -3
- package/test/fork/ForkTestBase.sol +617 -0
- package/test/fork/TestCashOutFork.t.sol +245 -0
- package/test/fork/TestLoanBorrowFork.t.sol +163 -0
- package/test/fork/TestLoanLiquidationFork.t.sol +129 -0
- package/test/fork/TestLoanReallocateFork.t.sol +103 -0
- package/test/fork/TestLoanRepayFork.t.sol +184 -0
- package/test/fork/TestSplitWeightFork.t.sol +186 -0
- package/test/mock/MockBuybackDataHook.sol +11 -4
- package/test/mock/MockBuybackDataHookMintPath.sol +11 -3
- package/test/regression/TestI20_CumulativeLoanCounter.t.sol +6 -5
- package/test/regression/TestL27_LiquidateGapHandling.t.sol +7 -6
- package/SECURITY.md +0 -68
|
@@ -30,7 +30,7 @@ import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/
|
|
|
30
30
|
/// @notice Fuzz tests for REVDeployer multi-stage auto-issuance.
|
|
31
31
|
/// Tests stage ID computation consistency and multi-stage claiming behavior.
|
|
32
32
|
/// Stage IDs use block.timestamp + i which may mismatch actual ruleset IDs.
|
|
33
|
-
contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow
|
|
33
|
+
contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow {
|
|
34
34
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
35
35
|
|
|
36
36
|
REVDeployer REV_DEPLOYER;
|
|
@@ -55,7 +55,8 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow, JBTest {
|
|
|
55
55
|
|
|
56
56
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
57
57
|
HOOK_STORE = new JB721TiersHookStore();
|
|
58
|
-
EXAMPLE_HOOK =
|
|
58
|
+
EXAMPLE_HOOK =
|
|
59
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
59
60
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
60
61
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
61
62
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -67,7 +68,7 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow, JBTest {
|
|
|
67
68
|
FEE_PROJECT_ID,
|
|
68
69
|
HOOK_DEPLOYER,
|
|
69
70
|
PUBLISHER,
|
|
70
|
-
|
|
71
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
71
72
|
makeAddr("loans"),
|
|
72
73
|
TRUSTED_FORWARDER
|
|
73
74
|
);
|
|
@@ -165,9 +166,12 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow, JBTest {
|
|
|
165
166
|
|
|
166
167
|
/// @notice Deploy 3-stage revnet, advance time, claim auto-issuance from each stage.
|
|
167
168
|
function test_multiStage_allStagesClaimable() external {
|
|
169
|
+
// Save the deploy timestamp for absolute warp targets.
|
|
170
|
+
uint256 deployTs = block.timestamp;
|
|
171
|
+
|
|
168
172
|
(uint256 revnetId, uint256[] memory stageIds) = _deployMultiStageRevnet(3);
|
|
169
173
|
|
|
170
|
-
// Stage 0 starts at
|
|
174
|
+
// Stage 0 starts at deploy time — immediately claimable.
|
|
171
175
|
REV_DEPLOYER.autoIssueFor(revnetId, stageIds[0], multisig());
|
|
172
176
|
|
|
173
177
|
uint256 stage0Amount = (10_000) * decimalMultiplier;
|
|
@@ -175,8 +179,8 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow, JBTest {
|
|
|
175
179
|
IJBToken(jbTokens().tokenOf(revnetId)).balanceOf(multisig()), stage0Amount, "Stage 0 auto-issuance claimed"
|
|
176
180
|
);
|
|
177
181
|
|
|
178
|
-
// Stage 1 starts at
|
|
179
|
-
vm.warp(
|
|
182
|
+
// Stage 1 starts at deployTs + 180 days.
|
|
183
|
+
vm.warp(deployTs + 180 days);
|
|
180
184
|
REV_DEPLOYER.autoIssueFor(revnetId, stageIds[1], multisig());
|
|
181
185
|
|
|
182
186
|
uint256 stage1Amount = (11_000) * decimalMultiplier;
|
|
@@ -186,8 +190,8 @@ contract REVAutoIssuanceFuzz_Local is TestBaseWorkflow, JBTest {
|
|
|
186
190
|
"Stage 1 auto-issuance claimed"
|
|
187
191
|
);
|
|
188
192
|
|
|
189
|
-
// Stage 2 starts at
|
|
190
|
-
vm.warp(
|
|
193
|
+
// Stage 2 starts at deployTs + 360 days.
|
|
194
|
+
vm.warp(deployTs + 360 days);
|
|
191
195
|
REV_DEPLOYER.autoIssueFor(revnetId, stageIds[2], multisig());
|
|
192
196
|
|
|
193
197
|
uint256 stage2Amount = (12_000) * decimalMultiplier;
|
|
@@ -29,7 +29,7 @@ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressReg
|
|
|
29
29
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
30
30
|
|
|
31
31
|
/// @notice Regression tests for REVDeployer.
|
|
32
|
-
contract REVDeployerRegressions_Local is TestBaseWorkflow
|
|
32
|
+
contract REVDeployerRegressions_Local is TestBaseWorkflow {
|
|
33
33
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
34
34
|
|
|
35
35
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
@@ -56,7 +56,8 @@ contract REVDeployerRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
56
56
|
|
|
57
57
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
58
58
|
HOOK_STORE = new JB721TiersHookStore();
|
|
59
|
-
EXAMPLE_HOOK =
|
|
59
|
+
EXAMPLE_HOOK =
|
|
60
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
60
61
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
61
62
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
62
63
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -77,7 +78,7 @@ contract REVDeployerRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
77
78
|
FEE_PROJECT_ID,
|
|
78
79
|
HOOK_DEPLOYER,
|
|
79
80
|
PUBLISHER,
|
|
80
|
-
|
|
81
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
81
82
|
address(LOANS_CONTRACT),
|
|
82
83
|
TRUSTED_FORWARDER
|
|
83
84
|
);
|
|
@@ -53,7 +53,7 @@ struct InvincibilityProjectConfig {
|
|
|
53
53
|
// =========================================================================
|
|
54
54
|
// Section A + B: Property Verification & Economic Tests
|
|
55
55
|
// =========================================================================
|
|
56
|
-
contract REVInvincibility_PropertyTests is TestBaseWorkflow
|
|
56
|
+
contract REVInvincibility_PropertyTests is TestBaseWorkflow {
|
|
57
57
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
58
58
|
|
|
59
59
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
@@ -202,7 +202,8 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow, JBTest {
|
|
|
202
202
|
|
|
203
203
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
204
204
|
HOOK_STORE = new JB721TiersHookStore();
|
|
205
|
-
EXAMPLE_HOOK =
|
|
205
|
+
EXAMPLE_HOOK =
|
|
206
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
206
207
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
207
208
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
208
209
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -224,7 +225,7 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow, JBTest {
|
|
|
224
225
|
FEE_PROJECT_ID,
|
|
225
226
|
HOOK_DEPLOYER,
|
|
226
227
|
PUBLISHER,
|
|
227
|
-
|
|
228
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
228
229
|
address(LOANS_CONTRACT),
|
|
229
230
|
TRUSTED_FORWARDER
|
|
230
231
|
);
|
|
@@ -922,7 +923,7 @@ contract REVInvincibility_PropertyTests is TestBaseWorkflow, JBTest {
|
|
|
922
923
|
// =========================================================================
|
|
923
924
|
// Section C: Invariant Properties (6 invariants)
|
|
924
925
|
// =========================================================================
|
|
925
|
-
contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow
|
|
926
|
+
contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow {
|
|
926
927
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
927
928
|
|
|
928
929
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer_INV";
|
|
@@ -956,7 +957,8 @@ contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow, JBTest {
|
|
|
956
957
|
|
|
957
958
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
958
959
|
HOOK_STORE = new JB721TiersHookStore();
|
|
959
|
-
EXAMPLE_HOOK =
|
|
960
|
+
EXAMPLE_HOOK =
|
|
961
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
960
962
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
961
963
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
962
964
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -977,7 +979,7 @@ contract REVInvincibility_Invariants is StdInvariant, TestBaseWorkflow, JBTest {
|
|
|
977
979
|
FEE_PROJECT_ID,
|
|
978
980
|
HOOK_DEPLOYER,
|
|
979
981
|
PUBLISHER,
|
|
980
|
-
|
|
982
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
981
983
|
address(LOANS_CONTRACT),
|
|
982
984
|
TRUSTED_FORWARDER
|
|
983
985
|
);
|
|
@@ -11,6 +11,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
11
11
|
import {IREVLoans} from "../src/interfaces/IREVLoans.sol";
|
|
12
12
|
import {REVLoan} from "../src/structs/REVLoan.sol";
|
|
13
13
|
import {REVLoanSource} from "../src/structs/REVLoanSource.sol";
|
|
14
|
+
import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
|
|
14
15
|
|
|
15
16
|
/// @title REVInvincibilityHandler
|
|
16
17
|
/// @notice Stateful fuzzing handler for the revnet + loans interaction surface.
|
package/test/REVLifecycle.t.sol
CHANGED
|
@@ -30,7 +30,7 @@ import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressReg
|
|
|
30
30
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
31
31
|
|
|
32
32
|
/// @notice Full revnet lifecycle E2E: deploy 3-stage -> pay -> advance stages -> cash out.
|
|
33
|
-
contract REVLifecycle_Local is TestBaseWorkflow
|
|
33
|
+
contract REVLifecycle_Local is TestBaseWorkflow {
|
|
34
34
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
35
35
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
36
36
|
|
|
@@ -61,7 +61,8 @@ contract REVLifecycle_Local is TestBaseWorkflow, JBTest {
|
|
|
61
61
|
|
|
62
62
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
63
63
|
HOOK_STORE = new JB721TiersHookStore();
|
|
64
|
-
EXAMPLE_HOOK =
|
|
64
|
+
EXAMPLE_HOOK =
|
|
65
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
65
66
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
66
67
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
67
68
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -82,7 +83,7 @@ contract REVLifecycle_Local is TestBaseWorkflow, JBTest {
|
|
|
82
83
|
FEE_PROJECT_ID,
|
|
83
84
|
HOOK_DEPLOYER,
|
|
84
85
|
PUBLISHER,
|
|
85
|
-
|
|
86
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
86
87
|
address(LOANS_CONTRACT),
|
|
87
88
|
TRUSTED_FORWARDER
|
|
88
89
|
);
|
|
@@ -31,6 +31,7 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
|
31
31
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
32
32
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
33
33
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
34
|
+
import {JBTest} from "@bananapus/core-v6/test/helpers/JBTest.sol";
|
|
34
35
|
|
|
35
36
|
struct FeeProjectConfig {
|
|
36
37
|
REVConfig configuration;
|
|
@@ -252,7 +253,7 @@ contract REVLoansCallHandler is JBTest {
|
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
255
|
|
|
255
|
-
contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow
|
|
256
|
+
contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow {
|
|
256
257
|
// A library that parses the packed ruleset metadata into a friendlier format.
|
|
257
258
|
using JBRulesetMetadataResolver for JBRuleset;
|
|
258
259
|
|
|
@@ -475,7 +476,8 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow, JBTest {
|
|
|
475
476
|
|
|
476
477
|
HOOK_STORE = new JB721TiersHookStore();
|
|
477
478
|
|
|
478
|
-
EXAMPLE_HOOK =
|
|
479
|
+
EXAMPLE_HOOK =
|
|
480
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
479
481
|
|
|
480
482
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
481
483
|
|
|
@@ -499,7 +501,7 @@ contract InvariantREVLoansTests is StdInvariant, TestBaseWorkflow, JBTest {
|
|
|
499
501
|
FEE_PROJECT_ID,
|
|
500
502
|
HOOK_DEPLOYER,
|
|
501
503
|
PUBLISHER,
|
|
502
|
-
|
|
504
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
503
505
|
address(LOANS_CONTRACT),
|
|
504
506
|
TRUSTED_FORWARDER
|
|
505
507
|
);
|
|
@@ -176,7 +176,7 @@ struct AttackProjectConfig {
|
|
|
176
176
|
/// @title REVLoansAttacks
|
|
177
177
|
/// @notice Attack tests for REVLoans covering uint112 truncation, reentrancy,
|
|
178
178
|
/// collateral race conditions, liquidation edge cases, and fuzz testing.
|
|
179
|
-
contract REVLoansAttacks is TestBaseWorkflow
|
|
179
|
+
contract REVLoansAttacks is TestBaseWorkflow {
|
|
180
180
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
181
181
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
182
182
|
|
|
@@ -318,7 +318,8 @@ contract REVLoansAttacks is TestBaseWorkflow, JBTest {
|
|
|
318
318
|
|
|
319
319
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
320
320
|
HOOK_STORE = new JB721TiersHookStore();
|
|
321
|
-
EXAMPLE_HOOK =
|
|
321
|
+
EXAMPLE_HOOK =
|
|
322
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
322
323
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
323
324
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
324
325
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -345,7 +346,7 @@ contract REVLoansAttacks is TestBaseWorkflow, JBTest {
|
|
|
345
346
|
FEE_PROJECT_ID,
|
|
346
347
|
HOOK_DEPLOYER,
|
|
347
348
|
PUBLISHER,
|
|
348
|
-
|
|
349
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
349
350
|
address(LOANS_CONTRACT),
|
|
350
351
|
TRUSTED_FORWARDER
|
|
351
352
|
);
|
|
@@ -129,7 +129,7 @@ contract FakeTerminal is ERC165, IJBPayoutTerminal {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/// @notice Regression tests for REVLoans unvalidated source terminal.
|
|
132
|
-
contract REVLoansRegressions_Local is TestBaseWorkflow
|
|
132
|
+
contract REVLoansRegressions_Local is TestBaseWorkflow {
|
|
133
133
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
134
134
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
135
135
|
|
|
@@ -157,7 +157,8 @@ contract REVLoansRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
157
157
|
|
|
158
158
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
159
159
|
HOOK_STORE = new JB721TiersHookStore();
|
|
160
|
-
EXAMPLE_HOOK =
|
|
160
|
+
EXAMPLE_HOOK =
|
|
161
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
161
162
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
162
163
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
163
164
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -178,7 +179,7 @@ contract REVLoansRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
178
179
|
FEE_PROJECT_ID,
|
|
179
180
|
HOOK_DEPLOYER,
|
|
180
181
|
PUBLISHER,
|
|
181
|
-
|
|
182
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
182
183
|
address(LOANS_CONTRACT),
|
|
183
184
|
TRUSTED_FORWARDER
|
|
184
185
|
);
|
|
@@ -246,6 +247,9 @@ contract REVLoansRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
246
247
|
/// it is registered in the JBDirectory for the project.
|
|
247
248
|
/// @dev The fake terminal's useAllowanceOf is called, showing no directory check occurs.
|
|
248
249
|
/// In production, a malicious terminal could return fake amounts or misroute funds.
|
|
250
|
+
/// @notice Verifies that borrowFrom now rejects unregistered terminals.
|
|
251
|
+
/// @dev Previously this test demonstrated the vulnerability. After the fix,
|
|
252
|
+
/// borrowFrom reverts with REVLoans_InvalidTerminal before reaching the fake terminal.
|
|
249
253
|
function test_unvalidatedSourceTerminal() public {
|
|
250
254
|
// Step 1: User pays into the revnet to get tokens (collateral)
|
|
251
255
|
vm.prank(USER);
|
|
@@ -265,9 +269,8 @@ contract REVLoansRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
265
269
|
}
|
|
266
270
|
assertFalse(found, "fake terminal should NOT be in the directory");
|
|
267
271
|
|
|
268
|
-
// Step 3: Try to borrow using the fake terminal as the source
|
|
269
|
-
//
|
|
270
|
-
// is registered in the directory before calling useAllowanceOf on it.
|
|
272
|
+
// Step 3: Try to borrow using the fake terminal as the source.
|
|
273
|
+
// This now correctly reverts with REVLoans_InvalidTerminal.
|
|
271
274
|
REVLoanSource memory fakeSource =
|
|
272
275
|
REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: IJBPayoutTerminal(address(fakeTerminal))});
|
|
273
276
|
|
|
@@ -275,28 +278,14 @@ contract REVLoansRegressions_Local is TestBaseWorkflow, JBTest {
|
|
|
275
278
|
LOANS_CONTRACT.borrowableAmountFrom(REVNET_ID, tokens, 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
|
|
276
279
|
assertGt(borrowable, 0, "should have borrowable amount");
|
|
277
280
|
|
|
278
|
-
//
|
|
279
|
-
// is
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
vm.expectCall(
|
|
283
|
-
address(fakeTerminal),
|
|
284
|
-
abi.encodeWithSelector(
|
|
285
|
-
IJBTerminal.accountingContextForTokenOf.selector, REVNET_ID, JBConstants.NATIVE_TOKEN
|
|
286
|
-
)
|
|
281
|
+
// The borrow should revert with REVLoans_InvalidTerminal because the fake terminal
|
|
282
|
+
// is not registered in the directory. The fake terminal is never called.
|
|
283
|
+
vm.expectRevert(
|
|
284
|
+
abi.encodeWithSelector(REVLoans.REVLoans_InvalidTerminal.selector, address(fakeTerminal), REVNET_ID)
|
|
287
285
|
);
|
|
288
|
-
vm.expectCall(address(fakeTerminal), abi.encodeWithSelector(IJBPayoutTerminal.useAllowanceOf.selector));
|
|
289
286
|
|
|
290
|
-
// The borrow will reach the fake terminal (showing no validation),
|
|
291
|
-
// but will revert downstream when trying to transfer 0 - fees (underflow).
|
|
292
287
|
vm.prank(USER);
|
|
293
|
-
vm.expectRevert();
|
|
294
288
|
LOANS_CONTRACT.borrowFrom(REVNET_ID, fakeSource, borrowable, tokens, payable(USER), 500);
|
|
295
|
-
|
|
296
|
-
// If we reach here, both vm.expectCall checks passed:
|
|
297
|
-
// 1. accountingContextForTokenOf was called on the fake terminal
|
|
298
|
-
// 2. useAllowanceOf was called on the fake terminal
|
|
299
|
-
// This shows no directory validation before calling the source terminal
|
|
300
289
|
}
|
|
301
290
|
|
|
302
291
|
/// @notice Verify that the configured loan source (real terminal) is properly registered.
|
|
@@ -136,7 +136,7 @@ struct FeeRecoveryProjectConfig {
|
|
|
136
136
|
/// @notice Tests for the fee payment error recovery in REVLoans._addTo().
|
|
137
137
|
/// @dev When feeTerminal.pay() reverts, the borrower should receive the fee amount back
|
|
138
138
|
/// instead of losing it. For ERC-20 tokens, the dangling allowance must also be cleaned up.
|
|
139
|
-
contract REVLoansFeeRecovery is TestBaseWorkflow
|
|
139
|
+
contract REVLoansFeeRecovery is TestBaseWorkflow {
|
|
140
140
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
141
141
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
142
142
|
|
|
@@ -281,7 +281,8 @@ contract REVLoansFeeRecovery is TestBaseWorkflow, JBTest {
|
|
|
281
281
|
|
|
282
282
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
283
283
|
HOOK_STORE = new JB721TiersHookStore();
|
|
284
|
-
EXAMPLE_HOOK =
|
|
284
|
+
EXAMPLE_HOOK =
|
|
285
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
285
286
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
286
287
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
287
288
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -309,7 +310,7 @@ contract REVLoansFeeRecovery is TestBaseWorkflow, JBTest {
|
|
|
309
310
|
FEE_PROJECT_ID,
|
|
310
311
|
HOOK_DEPLOYER,
|
|
311
312
|
PUBLISHER,
|
|
312
|
-
|
|
313
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
313
314
|
address(LOANS_CONTRACT),
|
|
314
315
|
TRUSTED_FORWARDER
|
|
315
316
|
);
|
|
@@ -38,7 +38,7 @@ struct FeeProjectConfig {
|
|
|
38
38
|
REVSuckerDeploymentConfig suckerDeploymentConfiguration;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
contract REVLoansSourcedTests is TestBaseWorkflow
|
|
41
|
+
contract REVLoansSourcedTests is TestBaseWorkflow {
|
|
42
42
|
/// @notice the salts that are used to deploy the contracts.
|
|
43
43
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
44
44
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
@@ -262,7 +262,8 @@ contract REVLoansSourcedTests is TestBaseWorkflow, JBTest {
|
|
|
262
262
|
|
|
263
263
|
HOOK_STORE = new JB721TiersHookStore();
|
|
264
264
|
|
|
265
|
-
EXAMPLE_HOOK =
|
|
265
|
+
EXAMPLE_HOOK =
|
|
266
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
266
267
|
|
|
267
268
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
268
269
|
|
|
@@ -298,7 +299,7 @@ contract REVLoansSourcedTests is TestBaseWorkflow, JBTest {
|
|
|
298
299
|
FEE_PROJECT_ID,
|
|
299
300
|
HOOK_DEPLOYER,
|
|
300
301
|
PUBLISHER,
|
|
301
|
-
|
|
302
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
302
303
|
address(LOANS_CONTRACT),
|
|
303
304
|
TRUSTED_FORWARDER
|
|
304
305
|
);
|
|
@@ -35,7 +35,7 @@ struct FeeProjectConfig {
|
|
|
35
35
|
REVSuckerDeploymentConfig suckerDeploymentConfiguration;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
contract REVLoansUnsourcedTests is TestBaseWorkflow
|
|
38
|
+
contract REVLoansUnsourcedTests is TestBaseWorkflow {
|
|
39
39
|
/// @notice the salts that are used to deploy the contracts.
|
|
40
40
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
41
41
|
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
@@ -249,7 +249,8 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow, JBTest {
|
|
|
249
249
|
|
|
250
250
|
HOOK_STORE = new JB721TiersHookStore();
|
|
251
251
|
|
|
252
|
-
EXAMPLE_HOOK =
|
|
252
|
+
EXAMPLE_HOOK =
|
|
253
|
+
new JB721TiersHook(jbDirectory(), jbPermissions(), jbRulesets(), HOOK_STORE, jbSplits(), multisig());
|
|
253
254
|
|
|
254
255
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
255
256
|
|
|
@@ -273,7 +274,7 @@ contract REVLoansUnsourcedTests is TestBaseWorkflow, JBTest {
|
|
|
273
274
|
FEE_PROJECT_ID,
|
|
274
275
|
HOOK_DEPLOYER,
|
|
275
276
|
PUBLISHER,
|
|
276
|
-
|
|
277
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
277
278
|
address(LOANS_CONTRACT),
|
|
278
279
|
TRUSTED_FORWARDER
|
|
279
280
|
);
|