@rev-net/core-v6 0.0.12 → 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/AUDIT_INSTRUCTIONS.md +295 -0
- package/CHANGE_LOG.md +316 -0
- package/README.md +2 -2
- package/RISKS.md +180 -35
- package/SKILLS.md +1 -1
- package/USER_JOURNEYS.md +489 -0
- package/package.json +9 -9
- package/script/Deploy.s.sol +40 -6
- package/script/helpers/RevnetCoreDeploymentLib.sol +7 -1
- package/src/REVDeployer.sol +63 -47
- package/src/REVLoans.sol +51 -15
- package/src/interfaces/IREVDeployer.sol +0 -1
- 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 +1 -0
- 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 +132 -12
- package/test/REVAutoIssuanceFuzz.t.sol +23 -3
- package/test/REVDeployerRegressions.t.sol +35 -4
- package/test/REVInvincibility.t.sol +58 -8
- package/test/REVInvincibilityHandler.sol +29 -0
- package/test/REVLifecycle.t.sol +28 -3
- package/test/REVLoans.invariants.t.sol +52 -5
- package/test/REVLoansAttacks.t.sol +43 -5
- package/test/REVLoansFeeRecovery.t.sol +50 -11
- package/test/REVLoansFindings.t.sol +27 -3
- package/test/REVLoansRegressions.t.sol +25 -3
- package/test/REVLoansSourceFeeRecovery.t.sol +491 -0
- package/test/REVLoansSourced.t.sol +56 -7
- package/test/REVLoansUnSourced.t.sol +49 -5
- package/test/TestBurnHeldTokens.t.sol +32 -5
- package/test/TestCEIPattern.t.sol +26 -2
- package/test/TestCashOutCallerValidation.t.sol +30 -4
- package/test/TestConversionDocumentation.t.sol +26 -5
- package/test/TestCrossCurrencyReclaim.t.sol +584 -0
- package/test/TestCrossSourceReallocation.t.sol +26 -2
- package/test/TestERC2771MetaTx.t.sol +557 -0
- package/test/TestEmptyBuybackSpecs.t.sol +23 -3
- package/test/TestFlashLoanSurplus.t.sol +28 -3
- package/test/TestHookArrayOOB.t.sol +24 -4
- package/test/TestLiquidationBehavior.t.sol +26 -3
- package/test/TestLoanSourceRotation.t.sol +525 -0
- package/test/TestLongTailEconomics.t.sol +651 -0
- package/test/TestLowFindings.t.sol +65 -2
- package/test/TestMixedFixes.t.sol +28 -3
- 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 +24 -2
- package/test/TestSplitWeightE2E.t.sol +29 -2
- package/test/TestSplitWeightFork.t.sol +46 -7
- package/test/TestStageTransitionBorrowable.t.sol +24 -2
- package/test/TestSwapTerminalPermission.t.sol +23 -3
- package/test/TestUint112Overflow.t.sol +28 -2
- package/test/TestZeroRepayment.t.sol +26 -2
- package/test/fork/ForkTestBase.sol +46 -3
- package/test/fork/TestCashOutFork.t.sol +1 -1
- package/test/fork/TestLoanBorrowFork.t.sol +1 -0
- package/test/fork/TestLoanCrossRulesetFork.t.sol +3 -1
- 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 +1 -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 +27 -4
- package/test/regression/TestLiquidateGapHandling.t.sol +29 -4
- package/test/regression/TestZeroPriceFeed.t.sol +396 -0
|
@@ -0,0 +1,267 @@
|
|
|
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 {JBSingleAllowance} from "@bananapus/core-v6/src/structs/JBSingleAllowance.sol";
|
|
26
|
+
import {MockPriceFeed} from "@bananapus/core-v6/test/mock/MockPriceFeed.sol";
|
|
27
|
+
import {MockERC20} from "@bananapus/core-v6/test/mock/MockERC20.sol";
|
|
28
|
+
import {REVLoans} from "../../src/REVLoans.sol";
|
|
29
|
+
import {REVLoan} from "../../src/structs/REVLoan.sol";
|
|
30
|
+
import {REVStageConfig, REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
|
|
31
|
+
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
32
|
+
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
33
|
+
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
34
|
+
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
35
|
+
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
36
|
+
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
37
|
+
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
38
|
+
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
39
|
+
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
40
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
41
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
42
|
+
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
43
|
+
|
|
44
|
+
/// @notice Validates that borrowFrom() reverts with a clear error when the caller hasn't granted BURN_TOKENS
|
|
45
|
+
/// permission to the REVLoans contract.
|
|
46
|
+
/// @dev Without this upfront check, the transaction would revert deep in JBController.burnTokensOf with a
|
|
47
|
+
/// less informative JBPermissioned_Unauthorized error.
|
|
48
|
+
contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
49
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
50
|
+
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
51
|
+
|
|
52
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
53
|
+
REVDeployer REV_DEPLOYER;
|
|
54
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
55
|
+
JB721TiersHook EXAMPLE_HOOK;
|
|
56
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
57
|
+
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
58
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
59
|
+
IJB721TiersHookStore HOOK_STORE;
|
|
60
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
61
|
+
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
62
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
63
|
+
REVLoans LOANS_CONTRACT;
|
|
64
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
65
|
+
MockERC20 TOKEN;
|
|
66
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
67
|
+
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
68
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
69
|
+
CTPublisher PUBLISHER;
|
|
70
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
71
|
+
MockBuybackDataHook MOCK_BUYBACK;
|
|
72
|
+
|
|
73
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
74
|
+
uint256 FEE_PROJECT_ID;
|
|
75
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
76
|
+
uint256 REVNET_ID;
|
|
77
|
+
|
|
78
|
+
address user = makeAddr("user");
|
|
79
|
+
|
|
80
|
+
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
81
|
+
|
|
82
|
+
function setUp() public override {
|
|
83
|
+
super.setUp();
|
|
84
|
+
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
85
|
+
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
86
|
+
HOOK_STORE = new JB721TiersHookStore();
|
|
87
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
88
|
+
jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
|
|
89
|
+
);
|
|
90
|
+
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
91
|
+
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
92
|
+
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
93
|
+
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
94
|
+
TOKEN = new MockERC20("1/2 ETH", "1/2");
|
|
95
|
+
MockPriceFeed priceFeed = new MockPriceFeed(1e21, 6);
|
|
96
|
+
vm.prank(multisig());
|
|
97
|
+
jbPrices()
|
|
98
|
+
.addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
|
|
99
|
+
LOANS_CONTRACT = new REVLoans({
|
|
100
|
+
controller: jbController(),
|
|
101
|
+
projects: jbProjects(),
|
|
102
|
+
revId: FEE_PROJECT_ID,
|
|
103
|
+
owner: address(this),
|
|
104
|
+
permit2: permit2(),
|
|
105
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
106
|
+
});
|
|
107
|
+
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
108
|
+
jbController(),
|
|
109
|
+
SUCKER_REGISTRY,
|
|
110
|
+
FEE_PROJECT_ID,
|
|
111
|
+
HOOK_DEPLOYER,
|
|
112
|
+
PUBLISHER,
|
|
113
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
114
|
+
address(LOANS_CONTRACT),
|
|
115
|
+
TRUSTED_FORWARDER
|
|
116
|
+
);
|
|
117
|
+
vm.prank(multisig());
|
|
118
|
+
jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
|
|
119
|
+
_deployFeeProject();
|
|
120
|
+
_deployRevnet();
|
|
121
|
+
vm.deal(user, 100e18);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function _deployFeeProject() internal {
|
|
125
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](2);
|
|
126
|
+
acc[0] = JBAccountingContext({
|
|
127
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
128
|
+
});
|
|
129
|
+
acc[1] = JBAccountingContext({token: address(TOKEN), decimals: 6, currency: uint32(uint160(address(TOKEN)))});
|
|
130
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
131
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
132
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
133
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
134
|
+
splits[0].beneficiary = payable(multisig());
|
|
135
|
+
splits[0].percent = 10_000;
|
|
136
|
+
REVAutoIssuance[] memory ai = new REVAutoIssuance[](1);
|
|
137
|
+
ai[0] = REVAutoIssuance({chainId: uint32(block.chainid), count: uint104(70_000e18), beneficiary: multisig()});
|
|
138
|
+
stages[0] = REVStageConfig({
|
|
139
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
140
|
+
autoIssuances: ai,
|
|
141
|
+
splitPercent: 2000,
|
|
142
|
+
splits: splits,
|
|
143
|
+
initialIssuance: uint112(1000e18),
|
|
144
|
+
issuanceCutFrequency: 90 days,
|
|
145
|
+
issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
|
|
146
|
+
cashOutTaxRate: 6000,
|
|
147
|
+
extraMetadata: 0
|
|
148
|
+
});
|
|
149
|
+
REVConfig memory cfg = REVConfig({
|
|
150
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
151
|
+
description: REVDescription("Revnet", "$REV", "ipfs://test", "REV_TOKEN"),
|
|
152
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
153
|
+
splitOperator: multisig(),
|
|
154
|
+
stageConfigurations: stages
|
|
155
|
+
});
|
|
156
|
+
vm.prank(multisig());
|
|
157
|
+
REV_DEPLOYER.deployFor({
|
|
158
|
+
revnetId: FEE_PROJECT_ID,
|
|
159
|
+
configuration: cfg,
|
|
160
|
+
terminalConfigurations: tc,
|
|
161
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
162
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
163
|
+
}),
|
|
164
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
165
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function _deployRevnet() internal {
|
|
170
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](2);
|
|
171
|
+
acc[0] = JBAccountingContext({
|
|
172
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
173
|
+
});
|
|
174
|
+
acc[1] = JBAccountingContext({token: address(TOKEN), decimals: 6, currency: uint32(uint160(address(TOKEN)))});
|
|
175
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
176
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
177
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
178
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
179
|
+
splits[0].beneficiary = payable(multisig());
|
|
180
|
+
splits[0].percent = 10_000;
|
|
181
|
+
REVAutoIssuance[] memory ai = new REVAutoIssuance[](1);
|
|
182
|
+
ai[0] = REVAutoIssuance({chainId: uint32(block.chainid), count: uint104(70_000e18), beneficiary: multisig()});
|
|
183
|
+
stages[0] = REVStageConfig({
|
|
184
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
185
|
+
autoIssuances: ai,
|
|
186
|
+
splitPercent: 2000,
|
|
187
|
+
splits: splits,
|
|
188
|
+
initialIssuance: uint112(1000e18),
|
|
189
|
+
issuanceCutFrequency: 90 days,
|
|
190
|
+
issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
|
|
191
|
+
cashOutTaxRate: 6000,
|
|
192
|
+
extraMetadata: 0
|
|
193
|
+
});
|
|
194
|
+
REVConfig memory cfg = REVConfig({
|
|
195
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
196
|
+
description: REVDescription("NANA", "$NANA", "ipfs://test2", "NANA_TOKEN"),
|
|
197
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
198
|
+
splitOperator: multisig(),
|
|
199
|
+
stageConfigurations: stages
|
|
200
|
+
});
|
|
201
|
+
(REVNET_ID,) = REV_DEPLOYER.deployFor({
|
|
202
|
+
revnetId: 0,
|
|
203
|
+
configuration: cfg,
|
|
204
|
+
terminalConfigurations: tc,
|
|
205
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
206
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
|
|
207
|
+
}),
|
|
208
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
209
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// @notice borrowFrom should revert when the caller hasn't granted BURN_TOKENS permission.
|
|
214
|
+
/// @dev The controller enforces this with JBPermissioned_Unauthorized when burnTokensOf is called.
|
|
215
|
+
function test_borrowFrom_revertsWithoutBurnPermission() public {
|
|
216
|
+
// Pay into the revnet to get tokens.
|
|
217
|
+
vm.prank(user);
|
|
218
|
+
uint256 tokenCount =
|
|
219
|
+
jbMultiTerminal().pay{value: 5e18}(REVNET_ID, JBConstants.NATIVE_TOKEN, 5e18, user, 0, "", "");
|
|
220
|
+
require(tokenCount > 0, "Should have received tokens");
|
|
221
|
+
|
|
222
|
+
// Attempt to borrow WITHOUT granting BURN_TOKENS permission → should revert.
|
|
223
|
+
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
224
|
+
vm.prank(user);
|
|
225
|
+
vm.expectRevert(
|
|
226
|
+
abi.encodeWithSelector(
|
|
227
|
+
JBPermissioned.JBPermissioned_Unauthorized.selector,
|
|
228
|
+
user, // account (the token holder)
|
|
229
|
+
address(LOANS_CONTRACT), // sender (the contract trying to burn)
|
|
230
|
+
REVNET_ID, // projectId
|
|
231
|
+
JBPermissionIds.BURN_TOKENS // permissionId
|
|
232
|
+
)
|
|
233
|
+
);
|
|
234
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/// @notice borrowFrom should succeed when the caller has granted BURN_TOKENS permission.
|
|
238
|
+
function test_borrowFrom_succeedsWithBurnPermission() public {
|
|
239
|
+
// Pay into the revnet to get tokens.
|
|
240
|
+
vm.prank(user);
|
|
241
|
+
uint256 tokenCount =
|
|
242
|
+
jbMultiTerminal().pay{value: 5e18}(REVNET_ID, JBConstants.NATIVE_TOKEN, 5e18, user, 0, "", "");
|
|
243
|
+
require(tokenCount > 0, "Should have received tokens");
|
|
244
|
+
|
|
245
|
+
// Grant BURN_TOKENS permission to the loans contract via the real permissions system.
|
|
246
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
247
|
+
permissionIds[0] = JBPermissionIds.BURN_TOKENS;
|
|
248
|
+
vm.prank(user);
|
|
249
|
+
jbPermissions()
|
|
250
|
+
.setPermissionsFor({
|
|
251
|
+
account: user,
|
|
252
|
+
permissionsData: JBPermissionsData({
|
|
253
|
+
operator: address(LOANS_CONTRACT), projectId: uint64(REVNET_ID), permissionIds: permissionIds
|
|
254
|
+
})
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Borrow should now succeed.
|
|
258
|
+
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
259
|
+
vm.prank(user);
|
|
260
|
+
(uint256 loanId, REVLoan memory loan) =
|
|
261
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
262
|
+
|
|
263
|
+
assertTrue(loanId > 0, "Loan ID should be non-zero");
|
|
264
|
+
assertTrue(loan.createdAt > 0, "Loan should be created");
|
|
265
|
+
assertTrue(loan.amount > 0, "Loan amount should be non-zero");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -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";
|
|
@@ -31,30 +38,44 @@ import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStor
|
|
|
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";
|
|
33
40
|
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
34
|
-
import {REVCroptopAllowedPost} from "../../src/structs/REVCroptopAllowedPost.sol";
|
|
35
41
|
|
|
36
42
|
/// @notice totalLoansBorrowedFor is a cumulative counter, not an active loan count.
|
|
37
43
|
/// @dev The rename from numberOfLoansFor to totalLoansBorrowedFor clarifies that the counter only increments
|
|
38
44
|
/// and never decrements. Repaying or liquidating a loan does NOT reduce the counter. This test verifies that
|
|
39
45
|
/// the counter remains at its high-water mark after loans are fully repaid and after loans are liquidated.
|
|
40
46
|
contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
47
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
41
48
|
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
42
49
|
|
|
50
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
43
51
|
REVDeployer REV_DEPLOYER;
|
|
52
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
44
53
|
JB721TiersHook EXAMPLE_HOOK;
|
|
54
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
45
55
|
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
56
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
46
57
|
IJB721TiersHookStore HOOK_STORE;
|
|
58
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
47
59
|
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
60
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
48
61
|
REVLoans LOANS_CONTRACT;
|
|
62
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
49
63
|
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
64
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
50
65
|
CTPublisher PUBLISHER;
|
|
66
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
51
67
|
MockBuybackDataHook MOCK_BUYBACK;
|
|
52
68
|
|
|
69
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
53
70
|
uint256 FEE_PROJECT_ID;
|
|
71
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
54
72
|
uint256 REVNET_ID;
|
|
55
73
|
|
|
74
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
56
75
|
address USER1 = makeAddr("user1");
|
|
76
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
57
77
|
address USER2 = makeAddr("user2");
|
|
78
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
58
79
|
address USER3 = makeAddr("user3");
|
|
59
80
|
|
|
60
81
|
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
@@ -129,6 +150,7 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
129
150
|
extraMetadata: 0
|
|
130
151
|
});
|
|
131
152
|
REVConfig memory cfg = REVConfig({
|
|
153
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
132
154
|
description: REVDescription("Revnet", "$REV", "ipfs://test", "REV_TOKEN"),
|
|
133
155
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
134
156
|
splitOperator: multisig(),
|
|
@@ -172,6 +194,7 @@ contract TestCumulativeLoanCounter is TestBaseWorkflow {
|
|
|
172
194
|
extraMetadata: 0
|
|
173
195
|
});
|
|
174
196
|
REVConfig memory cfg = REVConfig({
|
|
197
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
175
198
|
description: REVDescription("NANA", "$NANA", "ipfs://test2", "NANA_TOKEN"),
|
|
176
199
|
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
177
200
|
splitOperator: multisig(),
|