@rev-net/core-v6 0.0.29 → 0.0.31
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 +19 -9
- package/ARCHITECTURE.md +3 -0
- package/AUDIT_INSTRUCTIONS.md +11 -1
- package/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/RISKS.md +28 -4
- package/SKILLS.md +2 -1
- package/USER_JOURNEYS.md +28 -3
- package/package.json +8 -8
- package/references/operations.md +1 -1
- package/script/Deploy.s.sol +26 -4
- package/src/REVDeployer.sol +4 -2
- package/src/REVHiddenTokens.sol +149 -0
- package/src/REVLoans.sol +192 -199
- package/src/REVOwner.sol +51 -14
- package/src/interfaces/IREVHiddenTokens.sol +53 -0
- package/src/interfaces/IREVLoans.sol +8 -6
- package/test/REV.integrations.t.sol +12 -2
- package/test/REVAutoIssuanceFuzz.t.sol +12 -2
- package/test/REVDeployerRegressions.t.sol +14 -3
- package/test/REVInvincibility.t.sol +27 -8
- package/test/REVInvincibilityHandler.sol +1 -1
- package/test/REVLifecycle.t.sol +14 -3
- package/test/REVLoans.invariants.t.sol +15 -4
- package/test/REVLoansAttacks.t.sol +19 -7
- package/test/REVLoansFeeRecovery.t.sol +24 -13
- package/test/REVLoansFindings.t.sol +16 -5
- package/test/REVLoansRegressions.t.sol +15 -4
- package/test/REVLoansSourceFeeRecovery.t.sol +16 -5
- package/test/REVLoansSourced.t.sol +60 -25
- package/test/REVLoansUnSourced.t.sol +15 -4
- package/test/TestBurnHeldTokens.t.sol +14 -3
- package/test/TestCEIPattern.t.sol +19 -7
- package/test/TestCashOutCallerValidation.t.sol +15 -4
- package/test/TestConversionDocumentation.t.sol +14 -3
- package/test/TestCrossCurrencyReclaim.t.sol +14 -3
- package/test/TestCrossSourceReallocation.t.sol +15 -4
- package/test/TestERC2771MetaTx.t.sol +18 -5
- package/test/TestEmptyBuybackSpecs.t.sol +14 -3
- package/test/TestFlashLoanSurplus.t.sol +15 -4
- package/test/TestHiddenTokens.t.sol +431 -0
- package/test/TestHookArrayOOB.t.sol +14 -3
- package/test/TestLiquidationBehavior.t.sol +16 -5
- package/test/TestLoanSourceRotation.t.sol +20 -7
- package/test/TestLoansCashOutDelay.t.sol +18 -7
- package/test/TestLongTailEconomics.t.sol +14 -3
- package/test/TestLowFindings.t.sol +25 -9
- package/test/TestMixedFixes.t.sol +19 -8
- package/test/TestPermit2Signatures.t.sol +15 -4
- package/test/TestReallocationSandwich.t.sol +16 -4
- package/test/TestRevnetRegressions.t.sol +16 -5
- package/test/TestSplitWeightAdjustment.t.sol +16 -4
- package/test/TestSplitWeightE2E.t.sol +18 -4
- package/test/TestSplitWeightFork.t.sol +16 -3
- package/test/TestStageTransitionBorrowable.t.sol +14 -3
- package/test/TestSwapTerminalPermission.t.sol +14 -3
- package/test/TestUint112Overflow.t.sol +15 -4
- package/test/TestZeroAmountLoanGuard.t.sol +15 -4
- package/test/TestZeroRepayment.t.sol +15 -4
- package/test/audit/CodexPhantomSurplusTerminal.t.sol +367 -0
- package/test/audit/LoanIdOverflowGuard.t.sol +16 -5
- package/test/audit/NemesisOperatorDelegation.t.sol +289 -0
- package/test/fork/ForkTestBase.sol +18 -4
- package/test/fork/TestLoanBorrowFork.t.sol +2 -1
- package/test/fork/TestLoanERC20Fork.t.sol +4 -2
- package/test/fork/TestLoanTransferFork.t.sol +12 -2
- package/test/helpers/MaliciousContracts.sol +1 -1
- package/test/mock/MockBuybackCashOutRecorder.sol +2 -0
- package/test/mock/MockBuybackDataHook.sol +3 -1
- package/test/mock/MockBuybackDataHookMintPath.sol +2 -0
- package/test/mock/MockSuckerRegistry.sol +17 -0
- package/test/regression/TestBurnPermissionRequired.t.sol +16 -5
- package/test/regression/TestCashOutBuybackFeeLeak.t.sol +16 -3
- package/test/regression/TestCrossRevnetLiquidation.t.sol +14 -3
- package/test/regression/TestCumulativeLoanCounter.t.sol +15 -4
- package/test/regression/TestLiquidateGapHandling.t.sol +15 -4
- package/test/regression/TestZeroPriceFeed.t.sol +17 -6
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import "forge-std/Test.sol";
|
|
5
|
+
import "@bananapus/core-v6/test/helpers/TestBaseWorkflow.sol";
|
|
6
|
+
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
7
|
+
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
8
|
+
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
9
|
+
import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
|
|
10
|
+
import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
11
|
+
import "@croptop/core-v6/src/CTPublisher.sol";
|
|
12
|
+
import "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
13
|
+
import "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
14
|
+
import "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
15
|
+
import "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
16
|
+
import "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
17
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
18
|
+
import "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
19
|
+
import "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
20
|
+
import "@bananapus/buyback-hook-v6/src/interfaces/IJBBuybackHookRegistry.sol";
|
|
21
|
+
import "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
22
|
+
import "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
23
|
+
import "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
24
|
+
import "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
|
|
25
|
+
import "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
26
|
+
import "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
27
|
+
import "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
28
|
+
|
|
29
|
+
import {MockBuybackDataHook} from "../mock/MockBuybackDataHook.sol";
|
|
30
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
31
|
+
import {REVDeployer} from "../../src/REVDeployer.sol";
|
|
32
|
+
import {REVHiddenTokens} from "../../src/REVHiddenTokens.sol";
|
|
33
|
+
import {REVLoans} from "../../src/REVLoans.sol";
|
|
34
|
+
import {REVOwner} from "../../src/REVOwner.sol";
|
|
35
|
+
import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
|
|
36
|
+
import {IREVHiddenTokens} from "../../src/interfaces/IREVHiddenTokens.sol";
|
|
37
|
+
import {REVConfig} from "../../src/structs/REVConfig.sol";
|
|
38
|
+
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
39
|
+
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
40
|
+
import {REVStageConfig} from "../../src/structs/REVStageConfig.sol";
|
|
41
|
+
import {REVAutoIssuance} from "../../src/structs/REVAutoIssuance.sol";
|
|
42
|
+
import {REVSuckerDeploymentConfig} from "../../src/structs/REVSuckerDeploymentConfig.sol";
|
|
43
|
+
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
44
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
45
|
+
|
|
46
|
+
contract NemesisOperatorDelegationTest is TestBaseWorkflow {
|
|
47
|
+
bytes32 internal constant REV_DEPLOYER_SALT = "REVDeployer";
|
|
48
|
+
bytes32 internal constant ERC20_SALT = "REV_TOKEN";
|
|
49
|
+
|
|
50
|
+
address internal constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
51
|
+
|
|
52
|
+
address internal USER = makeAddr("user");
|
|
53
|
+
address internal OPERATOR = makeAddr("operator");
|
|
54
|
+
|
|
55
|
+
REVDeployer internal REV_DEPLOYER;
|
|
56
|
+
REVOwner internal REV_OWNER;
|
|
57
|
+
REVHiddenTokens internal HIDDEN_TOKENS;
|
|
58
|
+
REVLoans internal LOANS;
|
|
59
|
+
JB721TiersHook internal EXAMPLE_HOOK;
|
|
60
|
+
IJB721TiersHookDeployer internal HOOK_DEPLOYER;
|
|
61
|
+
IJB721TiersHookStore internal HOOK_STORE;
|
|
62
|
+
IJBAddressRegistry internal ADDRESS_REGISTRY;
|
|
63
|
+
IJBSuckerRegistry internal SUCKER_REGISTRY;
|
|
64
|
+
CTPublisher internal PUBLISHER;
|
|
65
|
+
MockBuybackDataHook internal MOCK_BUYBACK;
|
|
66
|
+
|
|
67
|
+
uint256 internal FEE_PROJECT_ID;
|
|
68
|
+
uint256 internal REVNET_ID;
|
|
69
|
+
|
|
70
|
+
function setUp() public override {
|
|
71
|
+
super.setUp();
|
|
72
|
+
|
|
73
|
+
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
74
|
+
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
75
|
+
HOOK_STORE = new JB721TiersHookStore();
|
|
76
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
77
|
+
jbDirectory(),
|
|
78
|
+
jbPermissions(),
|
|
79
|
+
jbPrices(),
|
|
80
|
+
jbRulesets(),
|
|
81
|
+
HOOK_STORE,
|
|
82
|
+
jbSplits(),
|
|
83
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
84
|
+
multisig()
|
|
85
|
+
);
|
|
86
|
+
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
87
|
+
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
88
|
+
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
89
|
+
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
90
|
+
|
|
91
|
+
LOANS = new REVLoans({
|
|
92
|
+
controller: jbController(),
|
|
93
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
94
|
+
revId: FEE_PROJECT_ID,
|
|
95
|
+
owner: address(this),
|
|
96
|
+
permit2: permit2(),
|
|
97
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
98
|
+
});
|
|
99
|
+
HIDDEN_TOKENS = new REVHiddenTokens(jbController(), TRUSTED_FORWARDER);
|
|
100
|
+
REV_OWNER = new REVOwner(
|
|
101
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
102
|
+
jbDirectory(),
|
|
103
|
+
FEE_PROJECT_ID,
|
|
104
|
+
SUCKER_REGISTRY,
|
|
105
|
+
address(LOANS),
|
|
106
|
+
address(HIDDEN_TOKENS)
|
|
107
|
+
);
|
|
108
|
+
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
109
|
+
jbController(),
|
|
110
|
+
SUCKER_REGISTRY,
|
|
111
|
+
FEE_PROJECT_ID,
|
|
112
|
+
HOOK_DEPLOYER,
|
|
113
|
+
PUBLISHER,
|
|
114
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
115
|
+
address(LOANS),
|
|
116
|
+
TRUSTED_FORWARDER,
|
|
117
|
+
address(REV_OWNER)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
REV_OWNER.setDeployer(IREVDeployer(REV_DEPLOYER));
|
|
121
|
+
|
|
122
|
+
vm.prank(multisig());
|
|
123
|
+
jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
|
|
124
|
+
|
|
125
|
+
_deployFeeProject();
|
|
126
|
+
REVNET_ID = _deployRevnet();
|
|
127
|
+
|
|
128
|
+
vm.deal(USER, 100e18);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function test_openLoanOperatorCanRedirectBorrowedFunds() public {
|
|
132
|
+
uint256 userTokens = _payUserIntoRevnet(10e18);
|
|
133
|
+
_grantPermission(USER, REVNET_ID, address(LOANS), JBPermissionIds.BURN_TOKENS);
|
|
134
|
+
_grantPermission(USER, REVNET_ID, OPERATOR, JBPermissionIds.OPEN_LOAN);
|
|
135
|
+
|
|
136
|
+
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
137
|
+
uint256 operatorBalanceBefore = OPERATOR.balance;
|
|
138
|
+
|
|
139
|
+
vm.prank(OPERATOR);
|
|
140
|
+
(uint256 loanId,) = LOANS.borrowFrom(REVNET_ID, source, 0, userTokens / 2, payable(OPERATOR), 25, USER);
|
|
141
|
+
|
|
142
|
+
assertEq(LOANS.ownerOf(loanId), USER, "loan NFT stays with the holder");
|
|
143
|
+
assertGt(OPERATOR.balance, operatorBalanceBefore, "operator receives the borrowed funds");
|
|
144
|
+
assertLt(
|
|
145
|
+
jbController().TOKENS().totalBalanceOf(USER, REVNET_ID),
|
|
146
|
+
userTokens,
|
|
147
|
+
"holder lost collateral even though proceeds were redirected"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function test_revealTokensOperatorCanRedirectHiddenTokens() public {
|
|
152
|
+
uint256 userTokens = _payUserIntoRevnet(10e18);
|
|
153
|
+
uint256 hiddenCount = userTokens / 2;
|
|
154
|
+
|
|
155
|
+
_grantPermission(USER, REVNET_ID, address(HIDDEN_TOKENS), JBPermissionIds.BURN_TOKENS);
|
|
156
|
+
_grantPermission(USER, REVNET_ID, OPERATOR, JBPermissionIds.REVEAL_TOKENS);
|
|
157
|
+
|
|
158
|
+
vm.prank(USER);
|
|
159
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hiddenCount, USER);
|
|
160
|
+
|
|
161
|
+
vm.prank(OPERATOR);
|
|
162
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hiddenCount, OPERATOR, USER);
|
|
163
|
+
|
|
164
|
+
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(USER, REVNET_ID), 0, "holder hidden balance was consumed");
|
|
165
|
+
assertEq(
|
|
166
|
+
jbController().TOKENS().totalBalanceOf(OPERATOR, REVNET_ID),
|
|
167
|
+
hiddenCount,
|
|
168
|
+
"operator receives the holder's revealed tokens"
|
|
169
|
+
);
|
|
170
|
+
assertEq(
|
|
171
|
+
jbController().TOKENS().totalBalanceOf(USER, REVNET_ID),
|
|
172
|
+
userTokens - hiddenCount,
|
|
173
|
+
"holder does not get the revealed tokens back"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function _grantPermission(address account, uint256 revnetId, address operator, uint8 permissionId) internal {
|
|
178
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
179
|
+
permissionIds[0] = permissionId;
|
|
180
|
+
|
|
181
|
+
vm.prank(account);
|
|
182
|
+
jbPermissions()
|
|
183
|
+
.setPermissionsFor(
|
|
184
|
+
account,
|
|
185
|
+
JBPermissionsData({operator: operator, projectId: uint56(revnetId), permissionIds: permissionIds})
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function _payUserIntoRevnet(uint256 amount) internal returns (uint256 tokenCount) {
|
|
190
|
+
vm.prank(USER);
|
|
191
|
+
tokenCount = jbMultiTerminal().pay{value: amount}({
|
|
192
|
+
projectId: REVNET_ID,
|
|
193
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
194
|
+
amount: amount,
|
|
195
|
+
beneficiary: USER,
|
|
196
|
+
minReturnedTokens: 0,
|
|
197
|
+
memo: "",
|
|
198
|
+
metadata: ""
|
|
199
|
+
});
|
|
200
|
+
assertGt(tokenCount, 0, "payment should mint revnet tokens");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function _deployFeeProject() internal {
|
|
204
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
205
|
+
acc[0] = JBAccountingContext({
|
|
206
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
210
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
211
|
+
|
|
212
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
213
|
+
stages[0] = REVStageConfig({
|
|
214
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
215
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
216
|
+
splitPercent: 0,
|
|
217
|
+
splits: new JBSplit[](0),
|
|
218
|
+
initialIssuance: uint112(1000e18),
|
|
219
|
+
issuanceCutFrequency: 0,
|
|
220
|
+
issuanceCutPercent: 0,
|
|
221
|
+
cashOutTaxRate: 0,
|
|
222
|
+
extraMetadata: 0
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
REVConfig memory feeConfig = REVConfig({
|
|
226
|
+
description: REVDescription("Fee Revnet", "FEE", "", ERC20_SALT),
|
|
227
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
228
|
+
splitOperator: multisig(),
|
|
229
|
+
stageConfigurations: stages
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
vm.prank(multisig());
|
|
233
|
+
REV_DEPLOYER.deployFor({
|
|
234
|
+
revnetId: FEE_PROJECT_ID,
|
|
235
|
+
configuration: feeConfig,
|
|
236
|
+
terminalConfigurations: tc,
|
|
237
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
238
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
239
|
+
}),
|
|
240
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
241
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function _deployRevnet() internal returns (uint256 revnetId) {
|
|
246
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
247
|
+
acc[0] = JBAccountingContext({
|
|
248
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
252
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
253
|
+
|
|
254
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
255
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
256
|
+
splits[0].beneficiary = payable(multisig());
|
|
257
|
+
splits[0].percent = 10_000;
|
|
258
|
+
|
|
259
|
+
stages[0] = REVStageConfig({
|
|
260
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
261
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
262
|
+
splitPercent: 2000,
|
|
263
|
+
splits: splits,
|
|
264
|
+
initialIssuance: uint112(1000e18),
|
|
265
|
+
issuanceCutFrequency: 90 days,
|
|
266
|
+
issuanceCutPercent: JBConstants.MAX_WEIGHT_CUT_PERCENT / 2,
|
|
267
|
+
cashOutTaxRate: 6000,
|
|
268
|
+
extraMetadata: 0
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
REVConfig memory config = REVConfig({
|
|
272
|
+
description: REVDescription("Revnet", "REV", "", bytes32("REV_TOKEN_2")),
|
|
273
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
274
|
+
splitOperator: multisig(),
|
|
275
|
+
stageConfigurations: stages
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
(revnetId,) = REV_DEPLOYER.deployFor({
|
|
279
|
+
revnetId: 0,
|
|
280
|
+
configuration: config,
|
|
281
|
+
terminalConfigurations: tc,
|
|
282
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
283
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("REV")
|
|
284
|
+
}),
|
|
285
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
286
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -34,6 +34,8 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
34
34
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
35
35
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
36
36
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
37
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
38
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
37
39
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
38
40
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
39
41
|
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
@@ -44,6 +46,8 @@ import {JB721TierConfig} from "@bananapus/721-hook-v6/src/structs/JB721TierConfi
|
|
|
44
46
|
import {JB721TierConfigFlags} from "@bananapus/721-hook-v6/src/structs/JB721TierConfigFlags.sol";
|
|
45
47
|
import {JB721InitTiersConfig} from "@bananapus/721-hook-v6/src/structs/JB721InitTiersConfig.sol";
|
|
46
48
|
import {IJB721TokenUriResolver} from "@bananapus/721-hook-v6/src/interfaces/IJB721TokenUriResolver.sol";
|
|
49
|
+
import "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
50
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
47
51
|
import {REVDeploy721TiersHookConfig} from "../../src/structs/REVDeploy721TiersHookConfig.sol";
|
|
48
52
|
import {REVBaseline721HookConfig} from "../../src/structs/REVBaseline721HookConfig.sol";
|
|
49
53
|
import {REV721TiersHookFlags} from "../../src/structs/REV721TiersHookFlags.sol";
|
|
@@ -74,6 +78,7 @@ import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
|
|
|
74
78
|
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
|
|
75
79
|
import {REVOwner} from "../../src/REVOwner.sol";
|
|
76
80
|
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
81
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
77
82
|
|
|
78
83
|
/// @notice Helper that adds liquidity to a V4 pool via the unlock/callback pattern.
|
|
79
84
|
contract LiquidityHelper is IUnlockCallback {
|
|
@@ -299,7 +304,14 @@ abstract contract ForkTestBase is TestBaseWorkflow {
|
|
|
299
304
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
300
305
|
HOOK_STORE = new JB721TiersHookStore();
|
|
301
306
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
302
|
-
jbDirectory(),
|
|
307
|
+
jbDirectory(),
|
|
308
|
+
jbPermissions(),
|
|
309
|
+
jbPrices(),
|
|
310
|
+
jbRulesets(),
|
|
311
|
+
HOOK_STORE,
|
|
312
|
+
jbSplits(),
|
|
313
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
314
|
+
multisig()
|
|
303
315
|
);
|
|
304
316
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
305
317
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -328,7 +340,7 @@ abstract contract ForkTestBase is TestBaseWorkflow {
|
|
|
328
340
|
|
|
329
341
|
LOANS_CONTRACT = new REVLoans({
|
|
330
342
|
controller: jbController(),
|
|
331
|
-
|
|
343
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
332
344
|
revId: FEE_PROJECT_ID,
|
|
333
345
|
owner: address(this),
|
|
334
346
|
permit2: permit2(),
|
|
@@ -340,7 +352,8 @@ abstract contract ForkTestBase is TestBaseWorkflow {
|
|
|
340
352
|
jbDirectory(),
|
|
341
353
|
FEE_PROJECT_ID,
|
|
342
354
|
SUCKER_REGISTRY,
|
|
343
|
-
address(LOANS_CONTRACT)
|
|
355
|
+
address(LOANS_CONTRACT),
|
|
356
|
+
address(0)
|
|
344
357
|
);
|
|
345
358
|
|
|
346
359
|
REV_DEPLOYER = new REVDeployer{salt: "REVDeployer_Fork"}(
|
|
@@ -707,7 +720,8 @@ abstract contract ForkTestBase is TestBaseWorkflow {
|
|
|
707
720
|
minBorrowAmount: 0,
|
|
708
721
|
collateralCount: collateral,
|
|
709
722
|
beneficiary: payable(borrower),
|
|
710
|
-
prepaidFeePercent: prepaidFeePercent
|
|
723
|
+
prepaidFeePercent: prepaidFeePercent,
|
|
724
|
+
holder: borrower
|
|
711
725
|
});
|
|
712
726
|
}
|
|
713
727
|
}
|
|
@@ -97,7 +97,8 @@ contract TestLoanBorrowFork is ForkTestBase {
|
|
|
97
97
|
minBorrowAmount: 0,
|
|
98
98
|
collateralCount: borrowerTokens,
|
|
99
99
|
beneficiary: payable(BORROWER),
|
|
100
|
-
prepaidFeePercent: prepaidFeePercent
|
|
100
|
+
prepaidFeePercent: prepaidFeePercent,
|
|
101
|
+
holder: BORROWER
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
uint256 borrowerReceived = BORROWER.balance - borrowerEthBefore;
|
|
@@ -184,7 +184,8 @@ contract TestLoanERC20Fork is ForkTestBase {
|
|
|
184
184
|
minBorrowAmount: 0,
|
|
185
185
|
collateralCount: collateral,
|
|
186
186
|
beneficiary: payable(borrower),
|
|
187
|
-
prepaidFeePercent: prepaidFeePercent
|
|
187
|
+
prepaidFeePercent: prepaidFeePercent,
|
|
188
|
+
holder: borrower
|
|
188
189
|
});
|
|
189
190
|
}
|
|
190
191
|
|
|
@@ -263,7 +264,8 @@ contract TestLoanERC20Fork is ForkTestBase {
|
|
|
263
264
|
minBorrowAmount: 0,
|
|
264
265
|
collateralCount: borrowerTokens,
|
|
265
266
|
beneficiary: payable(BORROWER),
|
|
266
|
-
prepaidFeePercent: prepaidFeePercent
|
|
267
|
+
prepaidFeePercent: prepaidFeePercent,
|
|
268
|
+
holder: BORROWER
|
|
267
269
|
});
|
|
268
270
|
|
|
269
271
|
uint256 borrowerUsdcReceived = IERC20(USDC).balanceOf(BORROWER) - borrowerUsdcBefore;
|
|
@@ -3,6 +3,8 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
5
5
|
import "./ForkTestBase.sol";
|
|
6
|
+
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
7
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
6
8
|
|
|
7
9
|
/// @notice Fork tests for transferring loan NFTs and repaying from the new owner.
|
|
8
10
|
///
|
|
@@ -79,9 +81,17 @@ contract TestLoanTransferFork is ForkTestBase {
|
|
|
79
81
|
|
|
80
82
|
JBSingleAllowance memory allowance;
|
|
81
83
|
|
|
82
|
-
// Original borrower tries to repay — should revert with
|
|
84
|
+
// Original borrower tries to repay — should revert with JBPermissioned_Unauthorized.
|
|
83
85
|
vm.prank(BORROWER);
|
|
84
|
-
vm.expectRevert(
|
|
86
|
+
vm.expectRevert(
|
|
87
|
+
abi.encodeWithSelector(
|
|
88
|
+
JBPermissioned.JBPermissioned_Unauthorized.selector,
|
|
89
|
+
newOwner,
|
|
90
|
+
BORROWER,
|
|
91
|
+
revnetId,
|
|
92
|
+
JBPermissionIds.REPAY_LOAN
|
|
93
|
+
)
|
|
94
|
+
);
|
|
85
95
|
LOANS_CONTRACT.repayLoan{value: loan.amount * 2}({
|
|
86
96
|
loanId: loanId,
|
|
87
97
|
maxRepayBorrowAmount: loan.amount * 2,
|
|
@@ -162,7 +162,7 @@ contract SurplusInflator is ERC165, IJBPayoutTerminal {
|
|
|
162
162
|
shouldInflate = false;
|
|
163
163
|
// Try to borrow at the inflated surplus
|
|
164
164
|
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: realTerminal});
|
|
165
|
-
try loans.borrowFrom(revnetId, source, 0, 1e18, payable(address(this)), 25) {} catch {}
|
|
165
|
+
try loans.borrowFrom(revnetId, source, 0, 1e18, payable(address(this)), 25, address(this)) {} catch {}
|
|
166
166
|
}
|
|
167
167
|
return 0;
|
|
168
168
|
}
|
|
@@ -38,12 +38,14 @@ contract MockBuybackCashOutRecorder is IJBRulesetDataHook, IJBPayHook, IJBCashOu
|
|
|
38
38
|
uint256 cashOutTaxRate,
|
|
39
39
|
uint256 cashOutCount,
|
|
40
40
|
uint256 totalSupply,
|
|
41
|
+
uint256 effectiveSurplusValue,
|
|
41
42
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
42
43
|
)
|
|
43
44
|
{
|
|
44
45
|
cashOutTaxRate = JBConstants.MAX_CASH_OUT_TAX_RATE;
|
|
45
46
|
cashOutCount = context.cashOutCount;
|
|
46
47
|
totalSupply = context.totalSupply;
|
|
48
|
+
effectiveSurplusValue = 0;
|
|
47
49
|
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
48
50
|
hookSpecifications[0] = JBCashOutHookSpecification({
|
|
49
51
|
hook: IJBCashOutHook(address(this)),
|
|
@@ -44,16 +44,18 @@ contract MockBuybackDataHook is IJBRulesetDataHook, IJBPayHook {
|
|
|
44
44
|
uint256 cashOutTaxRate,
|
|
45
45
|
uint256 cashOutCount,
|
|
46
46
|
uint256 totalSupply,
|
|
47
|
+
uint256 effectiveSurplusValue,
|
|
47
48
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
48
49
|
)
|
|
49
50
|
{
|
|
50
51
|
cashOutTaxRate = cashOutTaxRateToReturn == 0 ? context.cashOutTaxRate : cashOutTaxRateToReturn;
|
|
51
52
|
cashOutCount = cashOutCountToReturn == 0 ? context.cashOutCount : cashOutCountToReturn;
|
|
52
53
|
totalSupply = totalSupplyToReturn == 0 ? context.totalSupply : totalSupplyToReturn;
|
|
54
|
+
effectiveSurplusValue = 0;
|
|
53
55
|
|
|
54
56
|
if (!shouldReturnCashOutHookSpec) {
|
|
55
57
|
hookSpecifications = new JBCashOutHookSpecification[](0);
|
|
56
|
-
return (cashOutTaxRate, cashOutCount, totalSupply, hookSpecifications);
|
|
58
|
+
return (cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, hookSpecifications);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
@@ -35,12 +35,14 @@ contract MockBuybackDataHookMintPath is IJBRulesetDataHook, IJBPayHook {
|
|
|
35
35
|
uint256 cashOutTaxRate,
|
|
36
36
|
uint256 cashOutCount,
|
|
37
37
|
uint256 totalSupply,
|
|
38
|
+
uint256 effectiveSurplusValue,
|
|
38
39
|
JBCashOutHookSpecification[] memory hookSpecifications
|
|
39
40
|
)
|
|
40
41
|
{
|
|
41
42
|
cashOutTaxRate = context.cashOutTaxRate;
|
|
42
43
|
cashOutCount = context.cashOutCount;
|
|
43
44
|
totalSupply = context.totalSupply;
|
|
45
|
+
effectiveSurplusValue = 0;
|
|
44
46
|
hookSpecifications = new JBCashOutHookSpecification[](0);
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
/// @notice Minimal mock that returns zeros for all cross-chain queries.
|
|
5
|
+
contract MockSuckerRegistry {
|
|
6
|
+
function isSuckerOf(uint256, address) external pure returns (bool) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function remoteTotalSupplyOf(uint256) external pure returns (uint256) {
|
|
11
|
+
return 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function remoteSurplusOf(uint256, uint256, uint256) external pure returns (uint256) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -35,6 +35,8 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
35
35
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
36
36
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
37
37
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
38
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
39
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
38
40
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
39
41
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
40
42
|
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
@@ -42,6 +44,7 @@ import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.
|
|
|
42
44
|
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
43
45
|
import {REVOwner} from "../../src/REVOwner.sol";
|
|
44
46
|
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
47
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
45
48
|
|
|
46
49
|
/// @notice Validates that borrowFrom() reverts with a clear error when the caller hasn't granted BURN_TOKENS
|
|
47
50
|
/// permission to the REVLoans contract.
|
|
@@ -89,7 +92,14 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
|
89
92
|
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
90
93
|
HOOK_STORE = new JB721TiersHookStore();
|
|
91
94
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
92
|
-
jbDirectory(),
|
|
95
|
+
jbDirectory(),
|
|
96
|
+
jbPermissions(),
|
|
97
|
+
jbPrices(),
|
|
98
|
+
jbRulesets(),
|
|
99
|
+
HOOK_STORE,
|
|
100
|
+
jbSplits(),
|
|
101
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
102
|
+
multisig()
|
|
93
103
|
);
|
|
94
104
|
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
95
105
|
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
@@ -102,7 +112,7 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
|
102
112
|
.addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
|
|
103
113
|
LOANS_CONTRACT = new REVLoans({
|
|
104
114
|
controller: jbController(),
|
|
105
|
-
|
|
115
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
106
116
|
revId: FEE_PROJECT_ID,
|
|
107
117
|
owner: address(this),
|
|
108
118
|
permit2: permit2(),
|
|
@@ -113,7 +123,8 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
|
113
123
|
jbDirectory(),
|
|
114
124
|
FEE_PROJECT_ID,
|
|
115
125
|
SUCKER_REGISTRY,
|
|
116
|
-
address(LOANS_CONTRACT)
|
|
126
|
+
address(LOANS_CONTRACT),
|
|
127
|
+
address(0)
|
|
117
128
|
);
|
|
118
129
|
|
|
119
130
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -247,7 +258,7 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
|
247
258
|
JBPermissionIds.BURN_TOKENS // permissionId
|
|
248
259
|
)
|
|
249
260
|
);
|
|
250
|
-
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
261
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
|
|
251
262
|
}
|
|
252
263
|
|
|
253
264
|
/// @notice borrowFrom should succeed when the caller has granted BURN_TOKENS permission.
|
|
@@ -274,7 +285,7 @@ contract TestBurnPermissionRequired is TestBaseWorkflow {
|
|
|
274
285
|
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
275
286
|
vm.prank(user);
|
|
276
287
|
(uint256 loanId, REVLoan memory loan) =
|
|
277
|
-
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
288
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
|
|
278
289
|
|
|
279
290
|
assertTrue(loanId > 0, "Loan ID should be non-zero");
|
|
280
291
|
assertTrue(loan.createdAt > 0, "Loan should be created");
|
|
@@ -21,7 +21,11 @@ import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
|
|
|
21
21
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
22
22
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
23
23
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
24
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
25
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
24
26
|
import {IJB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookDeployer.sol";
|
|
27
|
+
import "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
28
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
25
29
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
26
30
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
27
31
|
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
@@ -37,6 +41,7 @@ import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
|
37
41
|
import {MockBuybackCashOutRecorder} from "../mock/MockBuybackCashOutRecorder.sol";
|
|
38
42
|
import {REVOwner} from "../../src/REVOwner.sol";
|
|
39
43
|
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
44
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
40
45
|
|
|
41
46
|
/// @title TestCashOutBuybackFeeLeak
|
|
42
47
|
/// @notice Proves the buyback hook callback receives only the non-fee cashOutCount (not the full count).
|
|
@@ -69,7 +74,14 @@ contract TestCashOutBuybackFeeLeak is TestBaseWorkflow {
|
|
|
69
74
|
suckerRegistry = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
70
75
|
hookStore = new JB721TiersHookStore();
|
|
71
76
|
exampleHook = new JB721TiersHook(
|
|
72
|
-
jbDirectory(),
|
|
77
|
+
jbDirectory(),
|
|
78
|
+
jbPermissions(),
|
|
79
|
+
jbPrices(),
|
|
80
|
+
jbRulesets(),
|
|
81
|
+
hookStore,
|
|
82
|
+
jbSplits(),
|
|
83
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
84
|
+
multisig()
|
|
73
85
|
);
|
|
74
86
|
addressRegistry = new JBAddressRegistry();
|
|
75
87
|
hookDeployer = new JB721TiersHookDeployer(exampleHook, hookStore, addressRegistry, multisig());
|
|
@@ -77,7 +89,7 @@ contract TestCashOutBuybackFeeLeak is TestBaseWorkflow {
|
|
|
77
89
|
mockBuyback = new MockBuybackCashOutRecorder();
|
|
78
90
|
loans = new REVLoans({
|
|
79
91
|
controller: jbController(),
|
|
80
|
-
|
|
92
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
81
93
|
revId: feeProjectId,
|
|
82
94
|
owner: address(this),
|
|
83
95
|
permit2: permit2(),
|
|
@@ -89,7 +101,8 @@ contract TestCashOutBuybackFeeLeak is TestBaseWorkflow {
|
|
|
89
101
|
jbDirectory(),
|
|
90
102
|
feeProjectId,
|
|
91
103
|
IJBSuckerRegistry(address(suckerRegistry)),
|
|
92
|
-
address(loans)
|
|
104
|
+
address(loans),
|
|
105
|
+
address(0)
|
|
93
106
|
);
|
|
94
107
|
|
|
95
108
|
revDeployer = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|