@rev-net/core-v6 0.0.11 → 0.0.13
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 +7 -7
- package/ARCHITECTURE.md +11 -11
- package/AUDIT_INSTRUCTIONS.md +295 -0
- package/CHANGE_LOG.md +316 -0
- package/README.md +9 -6
- package/RISKS.md +180 -35
- package/SKILLS.md +9 -11
- package/STYLE_GUIDE.md +14 -1
- package/USER_JOURNEYS.md +489 -0
- package/package.json +9 -9
- package/script/Deploy.s.sol +124 -40
- package/script/helpers/RevnetCoreDeploymentLib.sol +19 -6
- package/src/REVDeployer.sol +183 -175
- package/src/REVLoans.sol +65 -28
- package/src/interfaces/IREVDeployer.sol +25 -23
- package/src/structs/REV721TiersHookFlags.sol +1 -0
- package/src/structs/REVAutoIssuance.sol +1 -0
- package/src/structs/REVBaseline721HookConfig.sol +1 -0
- package/src/structs/REVConfig.sol +1 -0
- package/src/structs/REVCroptopAllowedPost.sol +1 -0
- package/src/structs/REVDeploy721TiersHookConfig.sol +13 -14
- package/src/structs/REVDescription.sol +1 -0
- package/src/structs/REVLoan.sol +1 -0
- package/src/structs/REVLoanSource.sol +1 -0
- package/src/structs/REVStageConfig.sol +1 -0
- package/src/structs/REVSuckerDeploymentConfig.sol +1 -0
- package/test/REV.integrations.t.sol +148 -19
- package/test/REVAutoIssuanceFuzz.t.sol +31 -6
- package/test/REVDeployerRegressions.t.sol +47 -9
- package/test/REVInvincibility.t.sol +83 -19
- package/test/REVInvincibilityHandler.sol +29 -0
- package/test/REVLifecycle.t.sol +36 -6
- package/test/REVLoans.invariants.t.sol +64 -10
- package/test/REVLoansAttacks.t.sol +54 -9
- package/test/REVLoansFeeRecovery.t.sol +61 -15
- package/test/REVLoansFindings.t.sol +42 -9
- package/test/REVLoansRegressions.t.sol +33 -6
- package/test/REVLoansSourceFeeRecovery.t.sol +491 -0
- package/test/REVLoansSourced.t.sol +79 -17
- package/test/REVLoansUnSourced.t.sol +61 -10
- package/test/TestBurnHeldTokens.t.sol +47 -11
- package/test/TestCEIPattern.t.sol +37 -6
- package/test/TestCashOutCallerValidation.t.sol +41 -8
- package/test/TestConversionDocumentation.t.sol +50 -13
- package/test/TestCrossCurrencyReclaim.t.sol +584 -0
- package/test/TestCrossSourceReallocation.t.sol +37 -6
- package/test/TestERC2771MetaTx.t.sol +557 -0
- package/test/TestEmptyBuybackSpecs.t.sol +45 -10
- package/test/TestFlashLoanSurplus.t.sol +39 -7
- package/test/TestHookArrayOOB.t.sol +42 -13
- package/test/TestLiquidationBehavior.t.sol +37 -7
- package/test/TestLoanSourceRotation.t.sol +525 -0
- package/test/TestLongTailEconomics.t.sol +651 -0
- package/test/TestLowFindings.t.sol +80 -8
- package/test/TestMixedFixes.t.sol +43 -9
- package/test/TestPermit2Signatures.t.sol +657 -0
- package/test/TestReallocationSandwich.t.sol +384 -0
- package/test/TestRevnetRegressions.t.sol +324 -0
- package/test/TestSplitWeightAdjustment.t.sol +52 -13
- package/test/TestSplitWeightE2E.t.sol +53 -18
- package/test/TestSplitWeightFork.t.sol +66 -21
- package/test/TestStageTransitionBorrowable.t.sol +38 -6
- package/test/TestSwapTerminalPermission.t.sol +37 -7
- package/test/TestUint112Overflow.t.sol +39 -6
- package/test/TestZeroRepayment.t.sol +37 -6
- package/test/fork/ForkTestBase.sol +66 -17
- package/test/fork/TestCashOutFork.t.sol +9 -3
- package/test/fork/TestLoanBorrowFork.t.sol +1 -0
- package/test/fork/TestLoanCrossRulesetFork.t.sol +11 -3
- package/test/fork/TestLoanLiquidationFork.t.sol +1 -0
- package/test/fork/TestLoanReallocateFork.t.sol +1 -0
- package/test/fork/TestLoanRepayFork.t.sol +1 -0
- package/test/fork/TestLoanTransferFork.t.sol +133 -0
- package/test/fork/TestSplitWeightFork.t.sol +3 -0
- package/test/helpers/REVEmpty721Config.sol +46 -0
- package/test/mock/MockBuybackDataHook.sol +1 -0
- package/test/regression/TestBurnPermissionRequired.t.sol +267 -0
- package/test/regression/TestCrossRevnetLiquidation.t.sol +228 -0
- package/test/regression/TestCumulativeLoanCounter.t.sol +38 -8
- package/test/regression/TestLiquidateGapHandling.t.sol +40 -8
- package/test/regression/TestZeroPriceFeed.t.sol +396 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.26;
|
|
3
|
+
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
|
+
import "forge-std/Test.sol";
|
|
6
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
7
|
+
import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
8
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
9
|
+
import /* {*} from */ "./../../src/REVDeployer.sol";
|
|
10
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
11
|
+
import "@croptop/core-v6/src/CTPublisher.sol";
|
|
12
|
+
import {MockBuybackDataHook} from "./../mock/MockBuybackDataHook.sol";
|
|
13
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
14
|
+
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
15
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
16
|
+
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
17
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
18
|
+
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
19
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
20
|
+
import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
|
|
21
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
22
|
+
import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
23
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
24
|
+
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
25
|
+
import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
|
|
26
|
+
import {MockERC20} from "@bananapus/core-v6/test/mock/MockERC20.sol";
|
|
27
|
+
import {REVLoans} from "../../src/REVLoans.sol";
|
|
28
|
+
import {REVLoan} from "../../src/structs/REVLoan.sol";
|
|
29
|
+
import {REVStageConfig, REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
|
|
30
|
+
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
31
|
+
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
32
|
+
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
33
|
+
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
34
|
+
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
35
|
+
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
36
|
+
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
37
|
+
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
38
|
+
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
39
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
40
|
+
|
|
41
|
+
/// @notice Validates that liquidateExpiredLoansFrom rejects loan number ranges that would overflow into another
|
|
42
|
+
/// revnet's namespace.
|
|
43
|
+
/// @dev _generateLoanId computes: (revnetId * _ONE_TRILLION) + loanNumber. If startingLoanId + count > _ONE_TRILLION,
|
|
44
|
+
/// the generated loanId would collide with a different revnet's loans.
|
|
45
|
+
contract TestCrossRevnetLiquidation is TestBaseWorkflow {
|
|
46
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
47
|
+
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
48
|
+
|
|
49
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
50
|
+
REVDeployer REV_DEPLOYER;
|
|
51
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
52
|
+
JB721TiersHook EXAMPLE_HOOK;
|
|
53
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
54
|
+
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
55
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
56
|
+
IJB721TiersHookStore HOOK_STORE;
|
|
57
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
58
|
+
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
59
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
60
|
+
REVLoans LOANS_CONTRACT;
|
|
61
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
62
|
+
MockERC20 TOKEN;
|
|
63
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
64
|
+
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
65
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
66
|
+
CTPublisher PUBLISHER;
|
|
67
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
68
|
+
MockBuybackDataHook MOCK_BUYBACK;
|
|
69
|
+
|
|
70
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
71
|
+
uint256 FEE_PROJECT_ID;
|
|
72
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
73
|
+
uint256 REVNET_ID;
|
|
74
|
+
|
|
75
|
+
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
76
|
+
|
|
77
|
+
/// @dev _ONE_TRILLION mirrors the private constant in REVLoans.sol.
|
|
78
|
+
uint256 private constant _ONE_TRILLION = 1_000_000_000_000;
|
|
79
|
+
|
|
80
|
+
function setUp() public override {
|
|
81
|
+
super.setUp();
|
|
82
|
+
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
83
|
+
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
84
|
+
HOOK_STORE = new JB721TiersHookStore();
|
|
85
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
86
|
+
jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
|
|
87
|
+
);
|
|
88
|
+
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
89
|
+
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
90
|
+
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
91
|
+
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
92
|
+
TOKEN = new MockERC20("1/2 ETH", "1/2");
|
|
93
|
+
MockPriceFeed priceFeed = new MockPriceFeed(1e21, 6);
|
|
94
|
+
vm.prank(multisig());
|
|
95
|
+
jbPrices()
|
|
96
|
+
.addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
|
|
97
|
+
LOANS_CONTRACT = new REVLoans({
|
|
98
|
+
controller: jbController(),
|
|
99
|
+
projects: jbProjects(),
|
|
100
|
+
revId: FEE_PROJECT_ID,
|
|
101
|
+
owner: address(this),
|
|
102
|
+
permit2: permit2(),
|
|
103
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
104
|
+
});
|
|
105
|
+
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
106
|
+
jbController(),
|
|
107
|
+
SUCKER_REGISTRY,
|
|
108
|
+
FEE_PROJECT_ID,
|
|
109
|
+
HOOK_DEPLOYER,
|
|
110
|
+
PUBLISHER,
|
|
111
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
112
|
+
address(LOANS_CONTRACT),
|
|
113
|
+
TRUSTED_FORWARDER
|
|
114
|
+
);
|
|
115
|
+
vm.prank(multisig());
|
|
116
|
+
jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
|
|
117
|
+
_deployFeeProject();
|
|
118
|
+
_deployRevnet();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _deployFeeProject() internal {
|
|
122
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](2);
|
|
123
|
+
acc[0] = JBAccountingContext({
|
|
124
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
125
|
+
});
|
|
126
|
+
acc[1] = JBAccountingContext({token: address(TOKEN), decimals: 6, currency: uint32(uint160(address(TOKEN)))});
|
|
127
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
128
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
129
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
130
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
131
|
+
splits[0].beneficiary = payable(multisig());
|
|
132
|
+
splits[0].percent = 10_000;
|
|
133
|
+
REVAutoIssuance[] memory ai = new REVAutoIssuance[](1);
|
|
134
|
+
ai[0] = REVAutoIssuance({chainId: uint32(block.chainid), count: uint104(70_000e18), beneficiary: multisig()});
|
|
135
|
+
stages[0] = REVStageConfig({
|
|
136
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
137
|
+
autoIssuances: ai,
|
|
138
|
+
splitPercent: 2000,
|
|
139
|
+
splits: splits,
|
|
140
|
+
initialIssuance: uint112(1000e18),
|
|
141
|
+
issuanceCutFrequency: 90 days,
|
|
142
|
+
issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
|
|
143
|
+
cashOutTaxRate: 6000,
|
|
144
|
+
extraMetadata: 0
|
|
145
|
+
});
|
|
146
|
+
REVConfig memory cfg = REVConfig({
|
|
147
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
148
|
+
description: REVDescription("Revnet", "$REV", "ipfs://test", "REV_TOKEN"),
|
|
149
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
150
|
+
splitOperator: multisig(),
|
|
151
|
+
stageConfigurations: stages
|
|
152
|
+
});
|
|
153
|
+
vm.prank(multisig());
|
|
154
|
+
REV_DEPLOYER.deployFor({
|
|
155
|
+
revnetId: FEE_PROJECT_ID,
|
|
156
|
+
configuration: cfg,
|
|
157
|
+
terminalConfigurations: tc,
|
|
158
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
159
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
160
|
+
}),
|
|
161
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
162
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function _deployRevnet() internal {
|
|
167
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](2);
|
|
168
|
+
acc[0] = JBAccountingContext({
|
|
169
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
170
|
+
});
|
|
171
|
+
acc[1] = JBAccountingContext({token: address(TOKEN), decimals: 6, currency: uint32(uint160(address(TOKEN)))});
|
|
172
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
173
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
174
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
175
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
176
|
+
splits[0].beneficiary = payable(multisig());
|
|
177
|
+
splits[0].percent = 10_000;
|
|
178
|
+
REVAutoIssuance[] memory ai = new REVAutoIssuance[](1);
|
|
179
|
+
ai[0] = REVAutoIssuance({chainId: uint32(block.chainid), count: uint104(70_000e18), beneficiary: multisig()});
|
|
180
|
+
stages[0] = REVStageConfig({
|
|
181
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
182
|
+
autoIssuances: ai,
|
|
183
|
+
splitPercent: 2000,
|
|
184
|
+
splits: splits,
|
|
185
|
+
initialIssuance: uint112(1000e18),
|
|
186
|
+
issuanceCutFrequency: 90 days,
|
|
187
|
+
issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
|
|
188
|
+
cashOutTaxRate: 6000,
|
|
189
|
+
extraMetadata: 0
|
|
190
|
+
});
|
|
191
|
+
REVConfig memory cfg = REVConfig({
|
|
192
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
193
|
+
description: REVDescription("NANA", "$NANA", "ipfs://test2", "NANA_TOKEN"),
|
|
194
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
195
|
+
splitOperator: multisig(),
|
|
196
|
+
stageConfigurations: stages
|
|
197
|
+
});
|
|
198
|
+
(REVNET_ID,) = REV_DEPLOYER.deployFor({
|
|
199
|
+
revnetId: 0,
|
|
200
|
+
configuration: cfg,
|
|
201
|
+
terminalConfigurations: tc,
|
|
202
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
203
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
|
|
204
|
+
}),
|
|
205
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
206
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// @notice liquidateExpiredLoansFrom should revert when startingLoanId would overflow into another revnet's
|
|
211
|
+
/// namespace.
|
|
212
|
+
function test_liquidateExpiredLoans_revertsOnCrossRevnetOverflow() public {
|
|
213
|
+
vm.expectRevert(REVLoans.REVLoans_LoanIdOverflow.selector);
|
|
214
|
+
LOANS_CONTRACT.liquidateExpiredLoansFrom(REVNET_ID, _ONE_TRILLION, 1);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// @notice liquidateExpiredLoansFrom should work for valid ranges within the revnet's namespace.
|
|
218
|
+
function test_liquidateExpiredLoans_normalRangeStillWorks() public {
|
|
219
|
+
// Calling with a valid range on a revnet with no loans should simply do nothing (no revert).
|
|
220
|
+
LOANS_CONTRACT.liquidateExpiredLoansFrom(REVNET_ID, 1, 5);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/// @notice The boundary case where startingLoanId + count == _ONE_TRILLION should succeed.
|
|
224
|
+
function test_liquidateExpiredLoans_boundaryExactlyAtLimit() public {
|
|
225
|
+
// Exactly at the boundary (not over) should not revert.
|
|
226
|
+
LOANS_CONTRACT.liquidateExpiredLoansFrom(REVNET_ID, _ONE_TRILLION - 1, 1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -1,28 +1,35 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
4
5
|
import "forge-std/Test.sol";
|
|
6
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
7
|
import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
6
|
-
import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
8
|
+
// import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
9
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
7
10
|
import /* {*} from */ "./../../src/REVDeployer.sol";
|
|
11
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
8
12
|
import "@croptop/core-v6/src/CTPublisher.sol";
|
|
9
13
|
import {MockBuybackDataHook} from "./../mock/MockBuybackDataHook.sol";
|
|
14
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
10
15
|
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
16
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
11
17
|
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
18
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
12
19
|
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
20
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
13
21
|
import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
|
|
22
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
14
23
|
import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
15
24
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
16
25
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
17
26
|
import {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
18
27
|
import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
|
|
19
|
-
import {MockERC20} from "@bananapus/core-v6/test/mock/MockERC20.sol";
|
|
20
28
|
import {REVLoans} from "../../src/REVLoans.sol";
|
|
21
29
|
import {REVLoan} from "../../src/structs/REVLoan.sol";
|
|
22
30
|
import {REVStageConfig, REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
|
|
23
31
|
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
24
32
|
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
25
|
-
import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
|
|
26
33
|
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
27
34
|
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
28
35
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
@@ -30,29 +37,45 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
|
30
37
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
31
38
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
32
39
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
40
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
33
41
|
|
|
34
42
|
/// @notice totalLoansBorrowedFor is a cumulative counter, not an active loan count.
|
|
35
43
|
/// @dev The rename from numberOfLoansFor to totalLoansBorrowedFor clarifies that the counter only increments
|
|
36
44
|
/// and never decrements. Repaying or liquidating a loan does NOT reduce the counter. This test verifies that
|
|
37
45
|
/// the counter remains at its high-water mark after loans are fully repaid and after loans are liquidated.
|
|
38
46
|
contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
47
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
39
48
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
40
49
|
|
|
50
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
41
51
|
REVDeployer REV_DEPLOYER;
|
|
52
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
42
53
|
JB721TiersHook EXAMPLE_HOOK;
|
|
54
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
43
55
|
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
56
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
44
57
|
IJB721TiersHookStore HOOK_STORE;
|
|
58
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
45
59
|
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
60
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
46
61
|
REVLoans LOANS_CONTRACT;
|
|
62
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
47
63
|
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
64
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
48
65
|
CTPublisher PUBLISHER;
|
|
66
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
49
67
|
MockBuybackDataHook MOCK_BUYBACK;
|
|
50
68
|
|
|
69
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
51
70
|
uint256 FEE_PROJECT_ID;
|
|
71
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
52
72
|
uint256 REVNET_ID;
|
|
53
73
|
|
|
74
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
54
75
|
address USER1 = makeAddr("user1");
|
|
76
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
55
77
|
address USER2 = makeAddr("user2");
|
|
78
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
56
79
|
address USER3 = makeAddr("user3");
|
|
57
80
|
|
|
58
81
|
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
@@ -62,8 +85,9 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
62
85
|
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
63
86
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
64
87
|
HOOK_STORE = new JB721TiersHookStore();
|
|
65
|
-
EXAMPLE_HOOK =
|
|
66
|
-
|
|
88
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
89
|
+
jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
|
|
90
|
+
);
|
|
67
91
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
68
92
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
69
93
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -126,6 +150,7 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
126
150
|
extraMetadata: 0
|
|
127
151
|
});
|
|
128
152
|
REVConfig memory cfg = REVConfig({
|
|
153
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
129
154
|
description: REVDescription("Revnet", "$REV", "ipfs://test", "REV_TOKEN"),
|
|
130
155
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
131
156
|
splitOperator: multisig(),
|
|
@@ -138,7 +163,9 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
138
163
|
terminalConfigurations: tc,
|
|
139
164
|
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
140
165
|
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
141
|
-
})
|
|
166
|
+
}),
|
|
167
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
168
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
142
169
|
});
|
|
143
170
|
}
|
|
144
171
|
|
|
@@ -167,18 +194,21 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
167
194
|
extraMetadata: 0
|
|
168
195
|
});
|
|
169
196
|
REVConfig memory cfg = REVConfig({
|
|
197
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
170
198
|
description: REVDescription("NANA", "$NANA", "ipfs://test2", "NANA_TOKEN"),
|
|
171
199
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
172
200
|
splitOperator: multisig(),
|
|
173
201
|
stageConfigurations: stages
|
|
174
202
|
});
|
|
175
|
-
REVNET_ID = REV_DEPLOYER.deployFor({
|
|
203
|
+
(REVNET_ID,) = REV_DEPLOYER.deployFor({
|
|
176
204
|
revnetId: 0,
|
|
177
205
|
configuration: cfg,
|
|
178
206
|
terminalConfigurations: tc,
|
|
179
207
|
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
180
208
|
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
|
|
181
|
-
})
|
|
209
|
+
}),
|
|
210
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
211
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
182
212
|
});
|
|
183
213
|
}
|
|
184
214
|
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
pragma solidity 0.8.26;
|
|
3
3
|
|
|
4
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
4
5
|
import "forge-std/Test.sol";
|
|
6
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
7
|
import /* {*} from */ "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
6
|
-
import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
8
|
+
// import /* {*} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
9
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
7
10
|
import /* {*} from */ "./../../src/REVDeployer.sol";
|
|
11
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
8
12
|
import "@croptop/core-v6/src/CTPublisher.sol";
|
|
9
13
|
import {MockBuybackDataHook} from "./../mock/MockBuybackDataHook.sol";
|
|
14
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
10
15
|
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
16
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
11
17
|
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
18
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
12
19
|
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
20
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
13
21
|
import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
|
|
22
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
14
23
|
import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
15
24
|
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
16
25
|
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
@@ -22,7 +31,6 @@ import {REVLoan} from "../../src/structs/REVLoan.sol";
|
|
|
22
31
|
import {REVStageConfig, REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
|
|
23
32
|
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
24
33
|
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
25
|
-
import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
|
|
26
34
|
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
27
35
|
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
28
36
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
@@ -30,31 +38,47 @@ import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
|
30
38
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
31
39
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
32
40
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
33
|
-
import {
|
|
41
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
34
42
|
|
|
35
43
|
/// @notice liquidateExpiredLoansFrom halts on deleted loan gaps.
|
|
36
44
|
/// @dev Before the fix, the function used `break` when encountering a deleted loan (createdAt == 0),
|
|
37
45
|
/// which stopped the entire iteration. Expired loans after the gap were never liquidated.
|
|
38
46
|
/// After the fix, `continue` is used instead, so the loop skips gaps and keeps processing.
|
|
39
47
|
contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
48
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
40
49
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
41
50
|
|
|
51
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
42
52
|
REVDeployer REV_DEPLOYER;
|
|
53
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
43
54
|
JB721TiersHook EXAMPLE_HOOK;
|
|
55
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
44
56
|
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
57
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
45
58
|
IJB721TiersHookStore HOOK_STORE;
|
|
59
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
46
60
|
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
61
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
47
62
|
REVLoans LOANS_CONTRACT;
|
|
63
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
48
64
|
MockERC20 TOKEN;
|
|
65
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
49
66
|
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
67
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
50
68
|
CTPublisher PUBLISHER;
|
|
69
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
51
70
|
MockBuybackDataHook MOCK_BUYBACK;
|
|
52
71
|
|
|
72
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
53
73
|
uint256 FEE_PROJECT_ID;
|
|
74
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
54
75
|
uint256 REVNET_ID;
|
|
55
76
|
|
|
77
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
56
78
|
address USER1 = makeAddr("user1");
|
|
79
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
57
80
|
address USER2 = makeAddr("user2");
|
|
81
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
58
82
|
address USER3 = makeAddr("user3");
|
|
59
83
|
|
|
60
84
|
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
@@ -64,8 +88,9 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
|
64
88
|
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
65
89
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
66
90
|
HOOK_STORE = new JB721TiersHookStore();
|
|
67
|
-
EXAMPLE_HOOK =
|
|
68
|
-
|
|
91
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
92
|
+
jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
|
|
93
|
+
);
|
|
69
94
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
70
95
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
71
96
|
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
@@ -128,6 +153,7 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
|
128
153
|
extraMetadata: 0
|
|
129
154
|
});
|
|
130
155
|
REVConfig memory cfg = REVConfig({
|
|
156
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
131
157
|
description: REVDescription("Revnet", "$REV", "ipfs://test", "REV_TOKEN"),
|
|
132
158
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
133
159
|
splitOperator: multisig(),
|
|
@@ -140,7 +166,9 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
|
140
166
|
terminalConfigurations: tc,
|
|
141
167
|
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
142
168
|
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
143
|
-
})
|
|
169
|
+
}),
|
|
170
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
171
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
144
172
|
});
|
|
145
173
|
}
|
|
146
174
|
|
|
@@ -170,18 +198,21 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
|
170
198
|
extraMetadata: 0
|
|
171
199
|
});
|
|
172
200
|
REVConfig memory cfg = REVConfig({
|
|
201
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
173
202
|
description: REVDescription("NANA", "$NANA", "ipfs://test2", "NANA_TOKEN"),
|
|
174
203
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
175
204
|
splitOperator: multisig(),
|
|
176
205
|
stageConfigurations: stages
|
|
177
206
|
});
|
|
178
|
-
REVNET_ID = REV_DEPLOYER.deployFor({
|
|
207
|
+
(REVNET_ID,) = REV_DEPLOYER.deployFor({
|
|
179
208
|
revnetId: 0,
|
|
180
209
|
configuration: cfg,
|
|
181
210
|
terminalConfigurations: tc,
|
|
182
211
|
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
183
212
|
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
|
|
184
|
-
})
|
|
213
|
+
}),
|
|
214
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
215
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
185
216
|
});
|
|
186
217
|
}
|
|
187
218
|
|
|
@@ -288,6 +319,7 @@ contract TestLiquidateGapHandling is TestBaseWorkflow {
|
|
|
288
319
|
/// Loan 1 and 4 should both be liquidated despite the double gap.
|
|
289
320
|
function test_liquidationHandlesMultipleConsecutiveGaps() public {
|
|
290
321
|
// Create 4 loans from the same user (simpler)
|
|
322
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
291
323
|
address USER4 = makeAddr("user4");
|
|
292
324
|
vm.deal(USER4, 100e18);
|
|
293
325
|
|