@rev-net/core-v6 0.0.29 → 0.0.30
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 +17 -3
- package/package.json +2 -2
- package/references/operations.md +1 -1
- package/script/Deploy.s.sol +25 -6
- package/src/REVHiddenTokens.sol +149 -0
- package/src/REVLoans.sol +115 -144
- package/src/REVOwner.sol +11 -3
- package/src/interfaces/IREVHiddenTokens.sol +53 -0
- package/src/interfaces/IREVLoans.sol +3 -6
- package/test/REV.integrations.t.sol +2 -1
- package/test/REVAutoIssuanceFuzz.t.sol +2 -1
- package/test/REVDeployerRegressions.t.sol +2 -2
- package/test/REVInvincibility.t.sol +6 -6
- package/test/REVInvincibilityHandler.sol +1 -1
- package/test/REVLifecycle.t.sol +2 -2
- package/test/REVLoans.invariants.t.sol +3 -3
- package/test/REVLoansAttacks.t.sol +7 -6
- package/test/REVLoansFeeRecovery.t.sol +12 -12
- package/test/REVLoansFindings.t.sol +4 -4
- package/test/REVLoansRegressions.t.sol +3 -3
- package/test/REVLoansSourceFeeRecovery.t.sol +4 -4
- package/test/REVLoansSourced.t.sol +48 -24
- package/test/REVLoansUnSourced.t.sol +3 -3
- package/test/TestBurnHeldTokens.t.sol +2 -2
- package/test/TestCEIPattern.t.sol +7 -6
- package/test/TestCashOutCallerValidation.t.sol +2 -2
- package/test/TestConversionDocumentation.t.sol +2 -2
- package/test/TestCrossCurrencyReclaim.t.sol +2 -2
- package/test/TestCrossSourceReallocation.t.sol +3 -3
- package/test/TestERC2771MetaTx.t.sol +6 -4
- package/test/TestEmptyBuybackSpecs.t.sol +2 -2
- package/test/TestFlashLoanSurplus.t.sol +3 -3
- package/test/TestHiddenTokens.t.sol +420 -0
- package/test/TestHookArrayOOB.t.sol +2 -2
- package/test/TestLiquidationBehavior.t.sol +4 -4
- package/test/TestLoanSourceRotation.t.sol +8 -6
- package/test/TestLoansCashOutDelay.t.sol +6 -6
- package/test/TestLongTailEconomics.t.sol +2 -2
- package/test/TestLowFindings.t.sol +13 -8
- package/test/TestMixedFixes.t.sol +7 -7
- package/test/TestPermit2Signatures.t.sol +3 -3
- package/test/TestReallocationSandwich.t.sol +4 -3
- package/test/TestRevnetRegressions.t.sol +3 -4
- package/test/TestSplitWeightAdjustment.t.sol +4 -3
- package/test/TestSplitWeightE2E.t.sol +4 -3
- package/test/TestSplitWeightFork.t.sol +2 -2
- package/test/TestStageTransitionBorrowable.t.sol +2 -2
- package/test/TestSwapTerminalPermission.t.sol +2 -2
- package/test/TestUint112Overflow.t.sol +3 -3
- package/test/TestZeroAmountLoanGuard.t.sol +3 -3
- package/test/TestZeroRepayment.t.sol +3 -3
- package/test/audit/LoanIdOverflowGuard.t.sol +4 -4
- package/test/audit/NemesisOperatorDelegation.t.sol +278 -0
- package/test/fork/ForkTestBase.sol +4 -3
- 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/regression/TestBurnPermissionRequired.t.sol +4 -4
- package/test/regression/TestCashOutBuybackFeeLeak.t.sol +2 -2
- package/test/regression/TestCrossRevnetLiquidation.t.sol +2 -2
- package/test/regression/TestCumulativeLoanCounter.t.sol +3 -3
- package/test/regression/TestLiquidateGapHandling.t.sol +3 -3
- package/test/regression/TestZeroPriceFeed.t.sol +5 -5
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
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
|
+
import {REVEmpty721Config} from "./helpers/REVEmpty721Config.sol";
|
|
14
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
15
|
+
import "@bananapus/core-v6/script/helpers/CoreDeploymentLib.sol";
|
|
16
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
17
|
+
import "@bananapus/721-hook-v6/script/helpers/Hook721DeploymentLib.sol";
|
|
18
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
19
|
+
import "@bananapus/suckers-v6/script/helpers/SuckerDeploymentLib.sol";
|
|
20
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
21
|
+
import "@croptop/core-v6/script/helpers/CroptopDeploymentLib.sol";
|
|
22
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
23
|
+
import "@bananapus/router-terminal-v6/script/helpers/RouterTerminalDeploymentLib.sol";
|
|
24
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
25
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
26
|
+
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
27
|
+
import {JBPermissionsData} from "@bananapus/core-v6/src/structs/JBPermissionsData.sol";
|
|
28
|
+
import {MockERC20} from "@bananapus/core-v6/test/mock/MockERC20.sol";
|
|
29
|
+
import {REVLoans} from "../src/REVLoans.sol";
|
|
30
|
+
import {REVHiddenTokens} from "../src/REVHiddenTokens.sol";
|
|
31
|
+
import {IREVHiddenTokens} from "../src/interfaces/IREVHiddenTokens.sol";
|
|
32
|
+
import {REVStageConfig, REVAutoIssuance} from "../src/structs/REVStageConfig.sol";
|
|
33
|
+
import {REVDescription} from "../src/structs/REVDescription.sol";
|
|
34
|
+
import {IREVLoans} from "./../src/interfaces/IREVLoans.sol";
|
|
35
|
+
import {JBSuckerDeployerConfig} from "@bananapus/suckers-v6/src/structs/JBSuckerDeployerConfig.sol";
|
|
36
|
+
import {JBSuckerRegistry} from "@bananapus/suckers-v6/src/JBSuckerRegistry.sol";
|
|
37
|
+
import {JB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/JB721TiersHookDeployer.sol";
|
|
38
|
+
import {JB721TiersHook} from "@bananapus/721-hook-v6/src/JB721TiersHook.sol";
|
|
39
|
+
import {JB721TiersHookStore} from "@bananapus/721-hook-v6/src/JB721TiersHookStore.sol";
|
|
40
|
+
import {JBAddressRegistry} from "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
41
|
+
import {IJBAddressRegistry} from "@bananapus/address-registry-v6/src/interfaces/IJBAddressRegistry.sol";
|
|
42
|
+
import {REVOwner} from "../src/REVOwner.sol";
|
|
43
|
+
import {IREVDeployer} from "../src/interfaces/IREVDeployer.sol";
|
|
44
|
+
|
|
45
|
+
/// @notice Tests for the standalone REVHiddenTokens contract.
|
|
46
|
+
contract TestHiddenTokens is TestBaseWorkflow {
|
|
47
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
48
|
+
bytes32 REV_DEPLOYER_SALT = "REVDeployer";
|
|
49
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
50
|
+
bytes32 ERC20_SALT = "REV_TOKEN";
|
|
51
|
+
|
|
52
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
53
|
+
REVDeployer REV_DEPLOYER;
|
|
54
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
55
|
+
REVOwner REV_OWNER;
|
|
56
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
57
|
+
JB721TiersHook EXAMPLE_HOOK;
|
|
58
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
59
|
+
IJB721TiersHookDeployer HOOK_DEPLOYER;
|
|
60
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
61
|
+
IJB721TiersHookStore HOOK_STORE;
|
|
62
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
63
|
+
IJBAddressRegistry ADDRESS_REGISTRY;
|
|
64
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
65
|
+
IREVLoans LOANS_CONTRACT;
|
|
66
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
67
|
+
REVHiddenTokens HIDDEN_TOKENS;
|
|
68
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
69
|
+
IJBSuckerRegistry SUCKER_REGISTRY;
|
|
70
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
71
|
+
CTPublisher PUBLISHER;
|
|
72
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
73
|
+
MockBuybackDataHook MOCK_BUYBACK;
|
|
74
|
+
|
|
75
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
76
|
+
uint256 FEE_PROJECT_ID;
|
|
77
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
78
|
+
uint256 REVNET_ID;
|
|
79
|
+
|
|
80
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
81
|
+
address USER = makeAddr("user");
|
|
82
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
83
|
+
address BENEFICIARY = makeAddr("beneficiary");
|
|
84
|
+
|
|
85
|
+
address private constant TRUSTED_FORWARDER = 0xB2b5841DBeF766d4b521221732F9B618fCf34A87;
|
|
86
|
+
|
|
87
|
+
function setUp() public override {
|
|
88
|
+
super.setUp();
|
|
89
|
+
FEE_PROJECT_ID = jbProjects().createFor(multisig());
|
|
90
|
+
SUCKER_REGISTRY = new JBSuckerRegistry(jbDirectory(), jbPermissions(), multisig(), address(0));
|
|
91
|
+
HOOK_STORE = new JB721TiersHookStore();
|
|
92
|
+
EXAMPLE_HOOK = new JB721TiersHook(
|
|
93
|
+
jbDirectory(), jbPermissions(), jbPrices(), jbRulesets(), HOOK_STORE, jbSplits(), multisig()
|
|
94
|
+
);
|
|
95
|
+
ADDRESS_REGISTRY = new JBAddressRegistry();
|
|
96
|
+
HOOK_DEPLOYER = new JB721TiersHookDeployer(EXAMPLE_HOOK, HOOK_STORE, ADDRESS_REGISTRY, multisig());
|
|
97
|
+
PUBLISHER = new CTPublisher(jbDirectory(), jbPermissions(), FEE_PROJECT_ID, multisig());
|
|
98
|
+
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
99
|
+
|
|
100
|
+
LOANS_CONTRACT = new REVLoans({
|
|
101
|
+
controller: jbController(),
|
|
102
|
+
revId: FEE_PROJECT_ID,
|
|
103
|
+
owner: address(this),
|
|
104
|
+
permit2: permit2(),
|
|
105
|
+
trustedForwarder: TRUSTED_FORWARDER
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
HIDDEN_TOKENS = new REVHiddenTokens(jbController(), TRUSTED_FORWARDER);
|
|
109
|
+
|
|
110
|
+
REV_OWNER = new REVOwner(
|
|
111
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
112
|
+
jbDirectory(),
|
|
113
|
+
FEE_PROJECT_ID,
|
|
114
|
+
SUCKER_REGISTRY,
|
|
115
|
+
address(LOANS_CONTRACT),
|
|
116
|
+
address(HIDDEN_TOKENS)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
120
|
+
jbController(),
|
|
121
|
+
SUCKER_REGISTRY,
|
|
122
|
+
FEE_PROJECT_ID,
|
|
123
|
+
HOOK_DEPLOYER,
|
|
124
|
+
PUBLISHER,
|
|
125
|
+
IJBBuybackHookRegistry(address(MOCK_BUYBACK)),
|
|
126
|
+
address(LOANS_CONTRACT),
|
|
127
|
+
TRUSTED_FORWARDER,
|
|
128
|
+
address(REV_OWNER)
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
REV_OWNER.setDeployer(REV_DEPLOYER);
|
|
132
|
+
|
|
133
|
+
vm.prank(multisig());
|
|
134
|
+
jbProjects().approve(address(REV_DEPLOYER), FEE_PROJECT_ID);
|
|
135
|
+
_deployFeeProject();
|
|
136
|
+
REVNET_ID = _deployRevnet();
|
|
137
|
+
vm.deal(USER, 100e18);
|
|
138
|
+
_grantBurnPermission(USER, REVNET_ID);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ──────────────────── Test: Hiding reduces totalSupply
|
|
142
|
+
// ────────────────────
|
|
143
|
+
|
|
144
|
+
function test_hideTokens_reducesTotalSupply() public {
|
|
145
|
+
// Pay to get tokens.
|
|
146
|
+
uint256 payAmount = 10e18;
|
|
147
|
+
vm.prank(USER);
|
|
148
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
149
|
+
projectId: REVNET_ID,
|
|
150
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
151
|
+
amount: payAmount,
|
|
152
|
+
beneficiary: USER,
|
|
153
|
+
minReturnedTokens: 0,
|
|
154
|
+
memo: "",
|
|
155
|
+
metadata: ""
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
159
|
+
assertGt(userTokens, 0, "User should have tokens after paying");
|
|
160
|
+
|
|
161
|
+
uint256 totalSupplyBefore = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
162
|
+
|
|
163
|
+
// Hide half the tokens.
|
|
164
|
+
uint256 hideCount = userTokens / 2;
|
|
165
|
+
vm.prank(USER);
|
|
166
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
167
|
+
|
|
168
|
+
uint256 totalSupplyAfter = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
169
|
+
assertEq(totalSupplyAfter, totalSupplyBefore - hideCount, "Total supply should decrease by hidden amount");
|
|
170
|
+
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(USER, REVNET_ID), hideCount, "Hidden balance should match");
|
|
171
|
+
assertEq(HIDDEN_TOKENS.totalHiddenOf(REVNET_ID), hideCount, "Total hidden should match");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ──────────────────── Test: Revealing restores tokens
|
|
175
|
+
// ────────────────────
|
|
176
|
+
|
|
177
|
+
function test_revealTokens_restoresTokens() public {
|
|
178
|
+
// Pay to get tokens.
|
|
179
|
+
uint256 payAmount = 10e18;
|
|
180
|
+
vm.prank(USER);
|
|
181
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
182
|
+
projectId: REVNET_ID,
|
|
183
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
184
|
+
amount: payAmount,
|
|
185
|
+
beneficiary: USER,
|
|
186
|
+
minReturnedTokens: 0,
|
|
187
|
+
memo: "",
|
|
188
|
+
metadata: ""
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
uint256 userTokensBefore = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
192
|
+
uint256 totalSupplyBefore = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
193
|
+
|
|
194
|
+
// Hide tokens.
|
|
195
|
+
uint256 hideCount = userTokensBefore / 2;
|
|
196
|
+
vm.prank(USER);
|
|
197
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
198
|
+
|
|
199
|
+
// Reveal tokens to beneficiary.
|
|
200
|
+
vm.prank(USER);
|
|
201
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount, BENEFICIARY, USER);
|
|
202
|
+
|
|
203
|
+
uint256 totalSupplyAfter = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
204
|
+
assertEq(totalSupplyAfter, totalSupplyBefore, "Total supply should be restored");
|
|
205
|
+
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(USER, REVNET_ID), 0, "Hidden balance should be zero");
|
|
206
|
+
assertEq(HIDDEN_TOKENS.totalHiddenOf(REVNET_ID), 0, "Total hidden should be zero");
|
|
207
|
+
assertEq(
|
|
208
|
+
jbController().TOKENS().totalBalanceOf(BENEFICIARY, REVNET_ID),
|
|
209
|
+
hideCount,
|
|
210
|
+
"Beneficiary should receive tokens"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ──────────────────── Test: Insufficient hidden balance reverts
|
|
215
|
+
// ────────────────────
|
|
216
|
+
|
|
217
|
+
function test_revealTokens_revertsOnInsufficientBalance() public {
|
|
218
|
+
// Pay to get tokens.
|
|
219
|
+
uint256 payAmount = 10e18;
|
|
220
|
+
vm.prank(USER);
|
|
221
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
222
|
+
projectId: REVNET_ID,
|
|
223
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
224
|
+
amount: payAmount,
|
|
225
|
+
beneficiary: USER,
|
|
226
|
+
minReturnedTokens: 0,
|
|
227
|
+
memo: "",
|
|
228
|
+
metadata: ""
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
232
|
+
uint256 hideCount = userTokens / 4;
|
|
233
|
+
|
|
234
|
+
// Hide some tokens.
|
|
235
|
+
vm.prank(USER);
|
|
236
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
237
|
+
|
|
238
|
+
// Try to reveal more than hidden — should revert.
|
|
239
|
+
vm.prank(USER);
|
|
240
|
+
vm.expectRevert(
|
|
241
|
+
abi.encodeWithSelector(
|
|
242
|
+
REVHiddenTokens.REVHiddenTokens_InsufficientHiddenBalance.selector, hideCount, hideCount + 1
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount + 1, USER, USER);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ──────────────────── Test: Hidden tokens inflate cash out rate
|
|
249
|
+
// ────────────────────
|
|
250
|
+
|
|
251
|
+
function test_hiddenTokens_inflateCashOutRate() public {
|
|
252
|
+
// Pay to get tokens for 2 users.
|
|
253
|
+
uint256 payAmount = 10e18;
|
|
254
|
+
vm.prank(USER);
|
|
255
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
256
|
+
projectId: REVNET_ID,
|
|
257
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
258
|
+
amount: payAmount,
|
|
259
|
+
beneficiary: USER,
|
|
260
|
+
minReturnedTokens: 0,
|
|
261
|
+
memo: "",
|
|
262
|
+
metadata: ""
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
266
|
+
|
|
267
|
+
// Hide half the user's tokens.
|
|
268
|
+
uint256 hideCount = userTokens / 2;
|
|
269
|
+
vm.prank(USER);
|
|
270
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
271
|
+
|
|
272
|
+
// The remaining tokens now represent a larger share of totalSupply.
|
|
273
|
+
uint256 totalSupply = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
274
|
+
uint256 remainingBalance = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
275
|
+
assertEq(remainingBalance, userTokens - hideCount, "Remaining balance should be half");
|
|
276
|
+
assertEq(totalSupply, userTokens - hideCount, "Total supply should equal remaining balance");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ──────────────────── Test: Events emitted correctly
|
|
280
|
+
// ────────────────────
|
|
281
|
+
|
|
282
|
+
function test_hideTokens_emitsEvent() public {
|
|
283
|
+
uint256 payAmount = 10e18;
|
|
284
|
+
vm.prank(USER);
|
|
285
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
286
|
+
projectId: REVNET_ID,
|
|
287
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
288
|
+
amount: payAmount,
|
|
289
|
+
beneficiary: USER,
|
|
290
|
+
minReturnedTokens: 0,
|
|
291
|
+
memo: "",
|
|
292
|
+
metadata: ""
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
296
|
+
|
|
297
|
+
vm.prank(USER);
|
|
298
|
+
vm.expectEmit(true, false, false, true);
|
|
299
|
+
emit IREVHiddenTokens.HideTokens(REVNET_ID, userTokens, USER, USER);
|
|
300
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, userTokens, USER);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function test_revealTokens_emitsEvent() public {
|
|
304
|
+
uint256 payAmount = 10e18;
|
|
305
|
+
vm.prank(USER);
|
|
306
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
307
|
+
projectId: REVNET_ID,
|
|
308
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
309
|
+
amount: payAmount,
|
|
310
|
+
beneficiary: USER,
|
|
311
|
+
minReturnedTokens: 0,
|
|
312
|
+
memo: "",
|
|
313
|
+
metadata: ""
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
317
|
+
|
|
318
|
+
vm.prank(USER);
|
|
319
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, userTokens, USER);
|
|
320
|
+
|
|
321
|
+
vm.prank(USER);
|
|
322
|
+
vm.expectEmit(true, false, false, true);
|
|
323
|
+
emit IREVHiddenTokens.RevealTokens(REVNET_ID, userTokens, BENEFICIARY, USER, USER);
|
|
324
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, userTokens, BENEFICIARY, USER);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ──────────────────── Internal helpers
|
|
328
|
+
// ────────────────────
|
|
329
|
+
|
|
330
|
+
function _grantBurnPermission(address account, uint256 revnetId) internal {
|
|
331
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
332
|
+
permissionIds[0] = JBPermissionIds.BURN_TOKENS;
|
|
333
|
+
JBPermissionsData memory permissionsData = JBPermissionsData({
|
|
334
|
+
operator: address(HIDDEN_TOKENS),
|
|
335
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
336
|
+
projectId: uint56(revnetId),
|
|
337
|
+
permissionIds: permissionIds
|
|
338
|
+
});
|
|
339
|
+
vm.prank(account);
|
|
340
|
+
jbPermissions().setPermissionsFor(account, permissionsData);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function _deployFeeProject() internal {
|
|
344
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
345
|
+
acc[0] = JBAccountingContext({
|
|
346
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
347
|
+
});
|
|
348
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
349
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
350
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
351
|
+
stages[0] = REVStageConfig({
|
|
352
|
+
startsAtOrAfter: uint48(block.timestamp),
|
|
353
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
354
|
+
splitPercent: 0,
|
|
355
|
+
splits: new JBSplit[](0),
|
|
356
|
+
initialIssuance: uint112(1000e18),
|
|
357
|
+
issuanceCutFrequency: 0,
|
|
358
|
+
issuanceCutPercent: 0,
|
|
359
|
+
cashOutTaxRate: 0,
|
|
360
|
+
extraMetadata: 0
|
|
361
|
+
});
|
|
362
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
363
|
+
REVConfig memory feeConfig = REVConfig({
|
|
364
|
+
description: REVDescription("Fee Revnet", "FEE", "", ERC20_SALT),
|
|
365
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
366
|
+
splitOperator: multisig(),
|
|
367
|
+
stageConfigurations: stages
|
|
368
|
+
});
|
|
369
|
+
vm.prank(multisig());
|
|
370
|
+
REV_DEPLOYER.deployFor({
|
|
371
|
+
revnetId: FEE_PROJECT_ID,
|
|
372
|
+
configuration: feeConfig,
|
|
373
|
+
terminalConfigurations: tc,
|
|
374
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
375
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("FEE")
|
|
376
|
+
}),
|
|
377
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
378
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function _deployRevnet() internal returns (uint256) {
|
|
383
|
+
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
384
|
+
acc[0] = JBAccountingContext({
|
|
385
|
+
token: JBConstants.NATIVE_TOKEN, decimals: 18, currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
386
|
+
});
|
|
387
|
+
JBTerminalConfig[] memory tc = new JBTerminalConfig[](1);
|
|
388
|
+
tc[0] = JBTerminalConfig({terminal: jbMultiTerminal(), accountingContextsToAccept: acc});
|
|
389
|
+
REVStageConfig[] memory stages = new REVStageConfig[](1);
|
|
390
|
+
stages[0] = REVStageConfig({
|
|
391
|
+
startsAtOrAfter: uint48(block.timestamp),
|
|
392
|
+
autoIssuances: new REVAutoIssuance[](0),
|
|
393
|
+
splitPercent: 0,
|
|
394
|
+
splits: new JBSplit[](0),
|
|
395
|
+
initialIssuance: uint112(1000e18),
|
|
396
|
+
issuanceCutFrequency: 0,
|
|
397
|
+
issuanceCutPercent: 0,
|
|
398
|
+
cashOutTaxRate: 5000, // 50% cash out tax rate
|
|
399
|
+
extraMetadata: 0
|
|
400
|
+
});
|
|
401
|
+
// forge-lint: disable-next-line(named-struct-fields)
|
|
402
|
+
REVConfig memory revConfig = REVConfig({
|
|
403
|
+
description: REVDescription("Test Revnet", "TEST", "", bytes32("TEST_TOKEN")),
|
|
404
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
405
|
+
splitOperator: multisig(),
|
|
406
|
+
stageConfigurations: stages
|
|
407
|
+
});
|
|
408
|
+
(uint256 revnetId,) = REV_DEPLOYER.deployFor({
|
|
409
|
+
revnetId: 0,
|
|
410
|
+
configuration: revConfig,
|
|
411
|
+
terminalConfigurations: tc,
|
|
412
|
+
suckerDeploymentConfiguration: REVSuckerDeploymentConfig({
|
|
413
|
+
deployerConfigurations: new JBSuckerDeployerConfig[](0), salt: keccak256("NANA")
|
|
414
|
+
}),
|
|
415
|
+
tiered721HookConfiguration: REVEmpty721Config.empty721Config(uint32(uint160(JBConstants.NATIVE_TOKEN))),
|
|
416
|
+
allowedPosts: REVEmpty721Config.emptyAllowedPosts()
|
|
417
|
+
});
|
|
418
|
+
return revnetId;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
@@ -92,7 +92,6 @@ contract TestHookArrayOOB is TestBaseWorkflow {
|
|
|
92
92
|
MOCK_BUYBACK = new MockBuybackDataHook();
|
|
93
93
|
LOANS_CONTRACT = new REVLoans({
|
|
94
94
|
controller: jbController(),
|
|
95
|
-
projects: jbProjects(),
|
|
96
95
|
revId: FEE_PROJECT_ID,
|
|
97
96
|
owner: address(this),
|
|
98
97
|
permit2: permit2(),
|
|
@@ -103,7 +102,8 @@ contract TestHookArrayOOB is TestBaseWorkflow {
|
|
|
103
102
|
jbDirectory(),
|
|
104
103
|
FEE_PROJECT_ID,
|
|
105
104
|
SUCKER_REGISTRY,
|
|
106
|
-
address(LOANS_CONTRACT)
|
|
105
|
+
address(LOANS_CONTRACT),
|
|
106
|
+
address(0)
|
|
107
107
|
);
|
|
108
108
|
|
|
109
109
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -100,7 +100,6 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
|
|
|
100
100
|
.addPriceFeedFor(0, uint32(uint160(address(TOKEN))), uint32(uint160(JBConstants.NATIVE_TOKEN)), priceFeed);
|
|
101
101
|
LOANS_CONTRACT = new REVLoans({
|
|
102
102
|
controller: jbController(),
|
|
103
|
-
projects: jbProjects(),
|
|
104
103
|
revId: FEE_PROJECT_ID,
|
|
105
104
|
owner: address(this),
|
|
106
105
|
permit2: permit2(),
|
|
@@ -111,7 +110,8 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
|
|
|
111
110
|
jbDirectory(),
|
|
112
111
|
FEE_PROJECT_ID,
|
|
113
112
|
SUCKER_REGISTRY,
|
|
114
|
-
address(LOANS_CONTRACT)
|
|
113
|
+
address(LOANS_CONTRACT),
|
|
114
|
+
address(0)
|
|
115
115
|
);
|
|
116
116
|
|
|
117
117
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -247,7 +247,7 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
|
|
|
247
247
|
);
|
|
248
248
|
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
249
249
|
vm.prank(user);
|
|
250
|
-
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), prepaidFee);
|
|
250
|
+
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), prepaidFee, user);
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/// @notice Verify that collateral is burned (not escrowed) — totalCollateralOf increases and loans contract holds
|
|
@@ -273,7 +273,7 @@ contract TestLiquidationBehavior is TestBaseWorkflow {
|
|
|
273
273
|
);
|
|
274
274
|
REVLoanSource memory source = REVLoanSource({token: JBConstants.NATIVE_TOKEN, terminal: jbMultiTerminal()});
|
|
275
275
|
vm.prank(USER);
|
|
276
|
-
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(USER), 25);
|
|
276
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(USER), 25, USER);
|
|
277
277
|
|
|
278
278
|
uint256 totalCollateralAfterBorrow = LOANS_CONTRACT.totalCollateralOf(REVNET_ID);
|
|
279
279
|
|
|
@@ -108,7 +108,6 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
108
108
|
|
|
109
109
|
LOANS_CONTRACT = new REVLoans({
|
|
110
110
|
controller: jbController(),
|
|
111
|
-
projects: jbProjects(),
|
|
112
111
|
revId: FEE_PROJECT_ID,
|
|
113
112
|
owner: address(this),
|
|
114
113
|
permit2: permit2(),
|
|
@@ -120,7 +119,8 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
120
119
|
jbDirectory(),
|
|
121
120
|
FEE_PROJECT_ID,
|
|
122
121
|
SUCKER_REGISTRY,
|
|
123
|
-
address(LOANS_CONTRACT)
|
|
122
|
+
address(LOANS_CONTRACT),
|
|
123
|
+
address(0)
|
|
124
124
|
);
|
|
125
125
|
|
|
126
126
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -271,7 +271,7 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
271
271
|
);
|
|
272
272
|
|
|
273
273
|
vm.prank(user);
|
|
274
|
-
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25);
|
|
274
|
+
(loanId,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, source, 0, tokenCount, payable(user), 25, user);
|
|
275
275
|
}
|
|
276
276
|
|
|
277
277
|
//*********************************************************************//
|
|
@@ -323,7 +323,8 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
323
323
|
);
|
|
324
324
|
|
|
325
325
|
vm.prank(user2);
|
|
326
|
-
(uint256 loanId2,) =
|
|
326
|
+
(uint256 loanId2,) =
|
|
327
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, tokenSource, 0, user2Tokens, payable(user2), 25, user2);
|
|
327
328
|
assertGt(loanId2, 0, "TOKEN loan should be created");
|
|
328
329
|
|
|
329
330
|
// Both sources should now be registered.
|
|
@@ -415,7 +416,8 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
415
416
|
);
|
|
416
417
|
|
|
417
418
|
vm.prank(user2);
|
|
418
|
-
(uint256 loanId2,) =
|
|
419
|
+
(uint256 loanId2,) =
|
|
420
|
+
LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource2, 0, user2Tokens, payable(user2), 25, user2);
|
|
419
421
|
assertGt(loanId2, 0, "second loan should be created");
|
|
420
422
|
|
|
421
423
|
// First loan should be unaffected.
|
|
@@ -530,7 +532,7 @@ contract TestLoanSourceRotation is TestBaseWorkflow {
|
|
|
530
532
|
);
|
|
531
533
|
|
|
532
534
|
vm.prank(user2);
|
|
533
|
-
(uint256 loanId2,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, tokens, payable(user2), 25);
|
|
535
|
+
(uint256 loanId2,) = LOANS_CONTRACT.borrowFrom(REVNET_ID, ethSource, 0, tokens, payable(user2), 25, user2);
|
|
534
536
|
assertGt(loanId2, 0, "second loan should succeed");
|
|
535
537
|
|
|
536
538
|
uint256 countAfterSecond = LOANS_CONTRACT.totalLoansBorrowedFor(REVNET_ID);
|
|
@@ -211,7 +211,6 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
211
211
|
|
|
212
212
|
LOANS_CONTRACT = new REVLoans({
|
|
213
213
|
controller: jbController(),
|
|
214
|
-
projects: jbProjects(),
|
|
215
214
|
revId: FEE_PROJECT_ID,
|
|
216
215
|
owner: address(this),
|
|
217
216
|
permit2: permit2(),
|
|
@@ -223,7 +222,8 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
223
222
|
jbDirectory(),
|
|
224
223
|
FEE_PROJECT_ID,
|
|
225
224
|
SUCKER_REGISTRY,
|
|
226
|
-
address(LOANS_CONTRACT)
|
|
225
|
+
address(LOANS_CONTRACT),
|
|
226
|
+
address(0)
|
|
227
227
|
);
|
|
228
228
|
|
|
229
229
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -347,7 +347,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
347
347
|
abi.encodeWithSelector(REVLoans.REVLoans_CashOutDelayNotFinished.selector, cashOutDelay, block.timestamp)
|
|
348
348
|
);
|
|
349
349
|
vm.prank(USER);
|
|
350
|
-
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, 1, tokenCount, payable(USER), 25);
|
|
350
|
+
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, 1, tokenCount, payable(USER), 25, USER);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
/// @notice After warping past the delay, borrowableAmountFrom should return a non-zero value.
|
|
@@ -393,7 +393,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
393
393
|
// Borrow — should succeed.
|
|
394
394
|
vm.prank(USER);
|
|
395
395
|
(uint256 loanId,) =
|
|
396
|
-
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, borrowable, tokenCount, payable(USER), 25);
|
|
396
|
+
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, borrowable, tokenCount, payable(USER), 25, USER);
|
|
397
397
|
assertGt(loanId, 0, "Should have created a loan");
|
|
398
398
|
}
|
|
399
399
|
|
|
@@ -431,7 +431,7 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
431
431
|
// Borrow — should succeed without any delay.
|
|
432
432
|
vm.prank(USER);
|
|
433
433
|
(uint256 loanId,) =
|
|
434
|
-
LOANS_CONTRACT.borrowFrom(NORMAL_REVNET_ID, source, borrowable, tokenCount, payable(USER), 25);
|
|
434
|
+
LOANS_CONTRACT.borrowFrom(NORMAL_REVNET_ID, source, borrowable, tokenCount, payable(USER), 25, USER);
|
|
435
435
|
assertGt(loanId, 0, "Should have created a loan");
|
|
436
436
|
}
|
|
437
437
|
|
|
@@ -477,6 +477,6 @@ contract TestLoansCashOutDelay is TestBaseWorkflow {
|
|
|
477
477
|
abi.encodeWithSelector(REVLoans.REVLoans_CashOutDelayNotFinished.selector, cashOutDelay, block.timestamp)
|
|
478
478
|
);
|
|
479
479
|
vm.prank(USER);
|
|
480
|
-
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, 1, tokenCount, payable(USER), 25);
|
|
480
|
+
LOANS_CONTRACT.borrowFrom(DELAYED_REVNET_ID, source, 1, tokenCount, payable(USER), 25, USER);
|
|
481
481
|
}
|
|
482
482
|
}
|
|
@@ -101,7 +101,6 @@ contract TestLongTailEconomics is TestBaseWorkflow {
|
|
|
101
101
|
|
|
102
102
|
LOANS_CONTRACT = new REVLoans({
|
|
103
103
|
controller: jbController(),
|
|
104
|
-
projects: jbProjects(),
|
|
105
104
|
revId: FEE_PROJECT_ID,
|
|
106
105
|
owner: address(this),
|
|
107
106
|
permit2: permit2(),
|
|
@@ -113,7 +112,8 @@ contract TestLongTailEconomics is TestBaseWorkflow {
|
|
|
113
112
|
jbDirectory(),
|
|
114
113
|
FEE_PROJECT_ID,
|
|
115
114
|
SUCKER_REGISTRY,
|
|
116
|
-
address(LOANS_CONTRACT)
|
|
115
|
+
address(LOANS_CONTRACT),
|
|
116
|
+
address(0)
|
|
117
117
|
);
|
|
118
118
|
|
|
119
119
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -290,7 +290,6 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
290
290
|
|
|
291
291
|
LOANS_CONTRACT = new REVLoans({
|
|
292
292
|
controller: jbController(),
|
|
293
|
-
projects: jbProjects(),
|
|
294
293
|
revId: FEE_PROJECT_ID,
|
|
295
294
|
owner: address(this),
|
|
296
295
|
permit2: permit2(),
|
|
@@ -302,7 +301,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
302
301
|
jbDirectory(),
|
|
303
302
|
FEE_PROJECT_ID,
|
|
304
303
|
SUCKER_REGISTRY,
|
|
305
|
-
address(LOANS_CONTRACT)
|
|
304
|
+
address(LOANS_CONTRACT),
|
|
305
|
+
address(0)
|
|
306
306
|
);
|
|
307
307
|
|
|
308
308
|
REV_DEPLOYER = new REVDeployer{salt: REV_DEPLOYER_SALT}(
|
|
@@ -388,7 +388,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
388
388
|
uint256 minPrepaid = LOANS_CONTRACT.MIN_PREPAID_FEE_PERCENT();
|
|
389
389
|
|
|
390
390
|
vm.prank(USER);
|
|
391
|
-
(uint256 loanId,) =
|
|
391
|
+
(uint256 loanId,) =
|
|
392
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, loanable, tokens, payable(USER), minPrepaid, USER);
|
|
392
393
|
|
|
393
394
|
REVLoan memory loanBefore = LOANS_CONTRACT.loanOf(loanId);
|
|
394
395
|
assertGt(loanBefore.amount, 0, "Loan should have an amount");
|
|
@@ -435,7 +436,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
435
436
|
uint256 minPrepaid = LOANS_CONTRACT.MIN_PREPAID_FEE_PERCENT();
|
|
436
437
|
|
|
437
438
|
vm.prank(USER);
|
|
438
|
-
(uint256 loanId,) =
|
|
439
|
+
(uint256 loanId,) =
|
|
440
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, loanable, tokens, payable(USER), minPrepaid, USER);
|
|
439
441
|
|
|
440
442
|
REVLoan memory loanBefore = LOANS_CONTRACT.loanOf(loanId);
|
|
441
443
|
assertGt(loanBefore.amount, 0, "Loan should exist before liquidation");
|
|
@@ -479,7 +481,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
479
481
|
uint256 minPrepaid = LOANS_CONTRACT.MIN_PREPAID_FEE_PERCENT();
|
|
480
482
|
|
|
481
483
|
vm.prank(USER);
|
|
482
|
-
(uint256 loanId,) =
|
|
484
|
+
(uint256 loanId,) =
|
|
485
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, loanable, tokens, payable(USER), minPrepaid, USER);
|
|
483
486
|
|
|
484
487
|
REVLoan memory loanBefore = LOANS_CONTRACT.loanOf(loanId);
|
|
485
488
|
assertGt(loanBefore.collateral, 1, "Need >1 collateral for partial return");
|
|
@@ -536,7 +539,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
536
539
|
uint256 minPrepaid = LOANS_CONTRACT.MIN_PREPAID_FEE_PERCENT();
|
|
537
540
|
|
|
538
541
|
vm.prank(USER);
|
|
539
|
-
(uint256 loanId,) =
|
|
542
|
+
(uint256 loanId,) =
|
|
543
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, loanable, tokens, payable(USER), minPrepaid, USER);
|
|
540
544
|
|
|
541
545
|
REVLoan memory loan = LOANS_CONTRACT.loanOf(loanId);
|
|
542
546
|
|
|
@@ -587,7 +591,8 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
587
591
|
|
|
588
592
|
// Borrow the full max against all tokens.
|
|
589
593
|
vm.prank(USER);
|
|
590
|
-
(uint256 loanId,) =
|
|
594
|
+
(uint256 loanId,) =
|
|
595
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, loanable, tokens, payable(USER), minPrepaid, USER);
|
|
591
596
|
|
|
592
597
|
REVLoan memory loanBefore = LOANS_CONTRACT.loanOf(loanId);
|
|
593
598
|
assertGt(loanBefore.collateral, 0, "Loan should have collateral");
|
|
@@ -656,6 +661,6 @@ contract TestLowFindings is TestBaseWorkflow {
|
|
|
656
661
|
// Attempt to borrow with 1 wei of collateral -- bonding curve returns 0, should revert.
|
|
657
662
|
vm.prank(USER);
|
|
658
663
|
vm.expectRevert(REVLoans.REVLoans_ZeroBorrowAmount.selector);
|
|
659
|
-
LOANS_CONTRACT.borrowFrom(revnetId, source, 0, 1, payable(USER), minPrepaid);
|
|
664
|
+
LOANS_CONTRACT.borrowFrom(revnetId, source, 0, 1, payable(USER), minPrepaid, USER);
|
|
660
665
|
}
|
|
661
666
|
}
|