@rev-net/core-v6 0.0.32 → 0.0.34
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 +48 -212
- package/ARCHITECTURE.md +67 -41
- package/AUDIT_INSTRUCTIONS.md +54 -108
- package/CHANGELOG.md +4 -3
- package/README.md +37 -30
- package/RISKS.md +49 -207
- package/SKILLS.md +15 -13
- package/USER_JOURNEYS.md +148 -63
- package/foundry.toml +1 -0
- package/package.json +36 -36
- package/references/operations.md +7 -0
- package/references/runtime.md +9 -0
- package/src/REVDeployer.sol +3 -2
- package/src/REVHiddenTokens.sol +48 -26
- package/src/REVLoans.sol +2 -2
- package/src/REVOwner.sol +10 -2
- package/src/interfaces/IREVHiddenTokens.sol +21 -6
- package/src/structs/REV721TiersHookFlags.sol +0 -1
- package/src/structs/REVAutoIssuance.sol +0 -1
- package/src/structs/REVBaseline721HookConfig.sol +0 -1
- package/src/structs/REVConfig.sol +0 -1
- package/src/structs/REVCroptopAllowedPost.sol +0 -1
- package/src/structs/REVDeploy721TiersHookConfig.sol +0 -1
- package/src/structs/REVDescription.sol +0 -1
- package/src/structs/REVLoan.sol +0 -1
- package/src/structs/REVLoanSource.sol +0 -1
- package/src/structs/REVStageConfig.sol +0 -1
- package/src/structs/REVSuckerDeploymentConfig.sol +0 -1
- package/test/TestAuditFixVerification.t.sol +675 -0
- package/test/TestHiddenTokens.t.sol +51 -8
- package/test/audit/CodexCrossChainBuybackRouteMismatch.t.sol +184 -0
- package/test/audit/NemesisOperatorDelegation.t.sol +77 -10
|
@@ -171,6 +171,8 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
171
171
|
|
|
172
172
|
uint256 totalSupplyBefore = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
173
173
|
|
|
174
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
175
|
+
|
|
174
176
|
// Hide half the tokens.
|
|
175
177
|
uint256 hideCount = userTokens / 2;
|
|
176
178
|
vm.prank(USER);
|
|
@@ -202,23 +204,23 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
202
204
|
uint256 userTokensBefore = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
203
205
|
uint256 totalSupplyBefore = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
204
206
|
|
|
207
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
208
|
+
|
|
205
209
|
// Hide tokens.
|
|
206
210
|
uint256 hideCount = userTokensBefore / 2;
|
|
207
211
|
vm.prank(USER);
|
|
208
212
|
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
209
213
|
|
|
210
|
-
// Reveal tokens to
|
|
214
|
+
// Reveal tokens back to USER.
|
|
211
215
|
vm.prank(USER);
|
|
212
|
-
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount,
|
|
216
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount, USER);
|
|
213
217
|
|
|
214
218
|
uint256 totalSupplyAfter = jbController().TOKENS().totalSupplyOf(REVNET_ID);
|
|
215
219
|
assertEq(totalSupplyAfter, totalSupplyBefore, "Total supply should be restored");
|
|
216
220
|
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(USER, REVNET_ID), 0, "Hidden balance should be zero");
|
|
217
221
|
assertEq(HIDDEN_TOKENS.totalHiddenOf(REVNET_ID), 0, "Total hidden should be zero");
|
|
218
222
|
assertEq(
|
|
219
|
-
jbController().TOKENS().totalBalanceOf(
|
|
220
|
-
hideCount,
|
|
221
|
-
"Beneficiary should receive tokens"
|
|
223
|
+
jbController().TOKENS().totalBalanceOf(USER, REVNET_ID), userTokensBefore, "User should receive tokens"
|
|
222
224
|
);
|
|
223
225
|
}
|
|
224
226
|
|
|
@@ -242,6 +244,8 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
242
244
|
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
243
245
|
uint256 hideCount = userTokens / 4;
|
|
244
246
|
|
|
247
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
248
|
+
|
|
245
249
|
// Hide some tokens.
|
|
246
250
|
vm.prank(USER);
|
|
247
251
|
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
@@ -253,7 +257,7 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
253
257
|
REVHiddenTokens.REVHiddenTokens_InsufficientHiddenBalance.selector, hideCount, hideCount + 1
|
|
254
258
|
)
|
|
255
259
|
);
|
|
256
|
-
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount + 1, USER
|
|
260
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount + 1, USER);
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
// ──────────────────── Test: Hidden tokens inflate cash out rate
|
|
@@ -275,6 +279,8 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
275
279
|
|
|
276
280
|
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
277
281
|
|
|
282
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
283
|
+
|
|
278
284
|
// Hide half the user's tokens.
|
|
279
285
|
uint256 hideCount = userTokens / 2;
|
|
280
286
|
vm.prank(USER);
|
|
@@ -305,6 +311,8 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
305
311
|
|
|
306
312
|
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
307
313
|
|
|
314
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
315
|
+
|
|
308
316
|
vm.prank(USER);
|
|
309
317
|
vm.expectEmit(true, false, false, true);
|
|
310
318
|
emit IREVHiddenTokens.HideTokens(REVNET_ID, userTokens, USER, USER);
|
|
@@ -326,13 +334,43 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
326
334
|
|
|
327
335
|
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
328
336
|
|
|
337
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
338
|
+
|
|
329
339
|
vm.prank(USER);
|
|
330
340
|
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, userTokens, USER);
|
|
331
341
|
|
|
332
342
|
vm.prank(USER);
|
|
333
343
|
vm.expectEmit(true, false, false, true);
|
|
334
|
-
emit IREVHiddenTokens.RevealTokens(REVNET_ID, userTokens,
|
|
335
|
-
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, userTokens,
|
|
344
|
+
emit IREVHiddenTokens.RevealTokens(REVNET_ID, userTokens, USER, USER);
|
|
345
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, userTokens, USER);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function test_setTokenHidingAllowedFor_allowsHolderToHide() public {
|
|
349
|
+
uint256 payAmount = 10e18;
|
|
350
|
+
|
|
351
|
+
vm.prank(USER);
|
|
352
|
+
jbMultiTerminal().pay{value: payAmount}({
|
|
353
|
+
projectId: REVNET_ID,
|
|
354
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
355
|
+
amount: payAmount,
|
|
356
|
+
beneficiary: USER,
|
|
357
|
+
minReturnedTokens: 0,
|
|
358
|
+
memo: "",
|
|
359
|
+
metadata: ""
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
uint256 userTokens = jbController().TOKENS().totalBalanceOf(USER, REVNET_ID);
|
|
363
|
+
uint256 hideCount = userTokens / 2;
|
|
364
|
+
|
|
365
|
+
_allowHolderToHide(USER, REVNET_ID);
|
|
366
|
+
|
|
367
|
+
vm.prank(USER);
|
|
368
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hideCount, USER);
|
|
369
|
+
|
|
370
|
+
vm.prank(USER);
|
|
371
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hideCount, USER);
|
|
372
|
+
|
|
373
|
+
assertEq(jbController().TOKENS().totalBalanceOf(USER, REVNET_ID), userTokens, "User balance should be restored");
|
|
336
374
|
}
|
|
337
375
|
|
|
338
376
|
// ──────────────────── Internal helpers
|
|
@@ -351,6 +389,11 @@ contract TestHiddenTokens is TestBaseWorkflow {
|
|
|
351
389
|
jbPermissions().setPermissionsFor(account, permissionsData);
|
|
352
390
|
}
|
|
353
391
|
|
|
392
|
+
function _allowHolderToHide(address holder, uint256 revnetId) internal {
|
|
393
|
+
vm.prank(address(REV_DEPLOYER));
|
|
394
|
+
HIDDEN_TOKENS.setTokenHidingAllowedFor(revnetId, holder, true);
|
|
395
|
+
}
|
|
396
|
+
|
|
354
397
|
function _deployFeeProject() internal {
|
|
355
398
|
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
356
399
|
acc[0] = JBAccountingContext({
|
|
@@ -0,0 +1,184 @@
|
|
|
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 {IJBBuybackHookRegistry} from "@bananapus/buyback-hook-v6/src/interfaces/IJBBuybackHookRegistry.sol";
|
|
7
|
+
import {IJBCashOutHook} from "@bananapus/core-v6/src/interfaces/IJBCashOutHook.sol";
|
|
8
|
+
import {IJBRulesetDataHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetDataHook.sol";
|
|
9
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
10
|
+
import {JBCashOuts} from "@bananapus/core-v6/src/libraries/JBCashOuts.sol";
|
|
11
|
+
import {JBBeforeCashOutRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
|
|
12
|
+
import {JBCashOutHookSpecification} from "@bananapus/core-v6/src/structs/JBCashOutHookSpecification.sol";
|
|
13
|
+
import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
|
|
14
|
+
import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
|
|
15
|
+
import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
|
|
16
|
+
import {JBRuleset} from "@bananapus/core-v6/src/structs/JBRuleset.sol";
|
|
17
|
+
import {IJBSuckerRegistry} from "@bananapus/suckers-v6/src/interfaces/IJBSuckerRegistry.sol";
|
|
18
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
19
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
20
|
+
|
|
21
|
+
import {REVOwner} from "../../src/REVOwner.sol";
|
|
22
|
+
|
|
23
|
+
contract ConfigurableSuckerRegistry {
|
|
24
|
+
uint256 public remoteSupply;
|
|
25
|
+
uint256 public remoteSurplus;
|
|
26
|
+
|
|
27
|
+
function setRemoteTotals(uint256 supply, uint256 surplus) external {
|
|
28
|
+
remoteSupply = supply;
|
|
29
|
+
remoteSurplus = surplus;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isSuckerOf(uint256, address) external pure returns (bool) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function remoteTotalSupplyOf(uint256) external view returns (uint256) {
|
|
37
|
+
return remoteSupply;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function remoteSurplusOf(uint256, uint256, uint256) external view returns (uint256) {
|
|
41
|
+
return remoteSurplus;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
contract ThresholdBuybackRegistry is IJBRulesetDataHook {
|
|
46
|
+
uint256 public immutable minimumSwapAmountOut;
|
|
47
|
+
|
|
48
|
+
constructor(uint256 minAmountOut) {
|
|
49
|
+
minimumSwapAmountOut = minAmountOut;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function beforeCashOutRecordedWith(JBBeforeCashOutRecordedContext calldata context)
|
|
53
|
+
external
|
|
54
|
+
view
|
|
55
|
+
returns (
|
|
56
|
+
uint256 cashOutTaxRate,
|
|
57
|
+
uint256 cashOutCount,
|
|
58
|
+
uint256 totalSupply,
|
|
59
|
+
uint256 effectiveSurplusValue,
|
|
60
|
+
JBCashOutHookSpecification[] memory hookSpecifications
|
|
61
|
+
)
|
|
62
|
+
{
|
|
63
|
+
uint256 directCashOutAmount = JBCashOuts.cashOutFrom({
|
|
64
|
+
surplus: context.surplus.value,
|
|
65
|
+
cashOutCount: context.cashOutCount,
|
|
66
|
+
totalSupply: context.totalSupply,
|
|
67
|
+
cashOutTaxRate: context.cashOutTaxRate
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
hookSpecifications = new JBCashOutHookSpecification[](1);
|
|
71
|
+
hookSpecifications[0] = JBCashOutHookSpecification({
|
|
72
|
+
hook: IJBCashOutHook(address(this)),
|
|
73
|
+
noop: directCashOutAmount >= minimumSwapAmountOut,
|
|
74
|
+
amount: 0,
|
|
75
|
+
metadata: ""
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
cashOutTaxRate =
|
|
79
|
+
directCashOutAmount >= minimumSwapAmountOut ? context.cashOutTaxRate : JBConstants.MAX_CASH_OUT_TAX_RATE;
|
|
80
|
+
cashOutCount = context.cashOutCount;
|
|
81
|
+
totalSupply = context.totalSupply;
|
|
82
|
+
effectiveSurplusValue = 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function beforePayRecordedWith(JBBeforePayRecordedContext calldata context)
|
|
86
|
+
external
|
|
87
|
+
pure
|
|
88
|
+
returns (uint256 weight, JBPayHookSpecification[] memory hookSpecifications)
|
|
89
|
+
{
|
|
90
|
+
weight = context.weight;
|
|
91
|
+
hookSpecifications = new JBPayHookSpecification[](0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hasMintPermissionFor(uint256, JBRuleset calldata, address) external pure returns (bool) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function setPoolFor(uint256, PoolKey calldata, uint256, address) external pure {}
|
|
99
|
+
|
|
100
|
+
function setPoolFor(uint256, uint24, int24, uint256, address) external pure {}
|
|
101
|
+
|
|
102
|
+
function initializePoolFor(uint256, uint24, int24, uint256, address, uint160) external pure {}
|
|
103
|
+
|
|
104
|
+
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
|
|
105
|
+
return interfaceId == type(IJBRulesetDataHook).interfaceId || interfaceId == type(IERC165).interfaceId;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
contract CodexCrossChainBuybackRouteMismatchTest is TestBaseWorkflow {
|
|
110
|
+
REVOwner internal ownerHook;
|
|
111
|
+
ConfigurableSuckerRegistry internal suckerRegistry;
|
|
112
|
+
ThresholdBuybackRegistry internal buybackRegistry;
|
|
113
|
+
|
|
114
|
+
function setUp() public override {
|
|
115
|
+
super.setUp();
|
|
116
|
+
|
|
117
|
+
suckerRegistry = new ConfigurableSuckerRegistry();
|
|
118
|
+
buybackRegistry = new ThresholdBuybackRegistry(50 ether);
|
|
119
|
+
|
|
120
|
+
ownerHook = new REVOwner(
|
|
121
|
+
IJBBuybackHookRegistry(address(buybackRegistry)),
|
|
122
|
+
jbDirectory(),
|
|
123
|
+
999_999,
|
|
124
|
+
IJBSuckerRegistry(address(suckerRegistry)),
|
|
125
|
+
address(0),
|
|
126
|
+
address(0)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function test_buybackRouteUsesOmnichainContextForRouting() public {
|
|
131
|
+
suckerRegistry.setRemoteTotals(0, 900 ether);
|
|
132
|
+
|
|
133
|
+
JBBeforeCashOutRecordedContext memory context = JBBeforeCashOutRecordedContext({
|
|
134
|
+
terminal: address(jbMultiTerminal()),
|
|
135
|
+
holder: address(0xBEEF),
|
|
136
|
+
projectId: 1,
|
|
137
|
+
rulesetId: 0,
|
|
138
|
+
cashOutCount: 100,
|
|
139
|
+
totalSupply: 1000,
|
|
140
|
+
surplus: JBTokenAmount({
|
|
141
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
142
|
+
value: 100 ether,
|
|
143
|
+
decimals: 18,
|
|
144
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
145
|
+
}),
|
|
146
|
+
useTotalSurplus: true,
|
|
147
|
+
cashOutTaxRate: 0,
|
|
148
|
+
beneficiaryIsFeeless: false,
|
|
149
|
+
metadata: ""
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
uint256 localDirectCashOut = JBCashOuts.cashOutFrom({
|
|
153
|
+
surplus: context.surplus.value,
|
|
154
|
+
cashOutCount: context.cashOutCount,
|
|
155
|
+
totalSupply: context.totalSupply,
|
|
156
|
+
cashOutTaxRate: context.cashOutTaxRate
|
|
157
|
+
});
|
|
158
|
+
uint256 omnichainDirectCashOut = JBCashOuts.cashOutFrom({
|
|
159
|
+
surplus: context.surplus.value + 900 ether,
|
|
160
|
+
cashOutCount: context.cashOutCount,
|
|
161
|
+
totalSupply: context.totalSupply,
|
|
162
|
+
cashOutTaxRate: context.cashOutTaxRate
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
assertLt(localDirectCashOut, 50 ether, "local route should prefer swap in the mock");
|
|
166
|
+
assertGe(omnichainDirectCashOut, 50 ether, "omnichain route should prefer direct reclaim");
|
|
167
|
+
|
|
168
|
+
(uint256 returnedTaxRate,, uint256 returnedSupply, uint256 returnedSurplus,) =
|
|
169
|
+
ownerHook.beforeCashOutRecordedWith(context);
|
|
170
|
+
|
|
171
|
+
// After the fix, REVOwner forwards the cross-chain-adjusted context to the buyback hook.
|
|
172
|
+
// The buyback hook now sees the full omnichain surplus (1000 ether) and correctly routes
|
|
173
|
+
// to direct reclaim (passthrough) instead of swap.
|
|
174
|
+
assertEq(returnedSupply, context.totalSupply, "owner returns cross-chain total supply");
|
|
175
|
+
assertEq(returnedSurplus, context.surplus.value + 900 ether, "owner returns cross-chain effective surplus");
|
|
176
|
+
// With omnichain context, the direct reclaim (100 ether) exceeds the threshold (50 ether),
|
|
177
|
+
// so the buyback hook chooses passthrough (returns the original cashOutTaxRate of 0).
|
|
178
|
+
assertEq(
|
|
179
|
+
returnedTaxRate,
|
|
180
|
+
context.cashOutTaxRate,
|
|
181
|
+
"routing correctly uses omnichain context - direct reclaim beats threshold"
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -148,32 +148,82 @@ contract NemesisOperatorDelegationTest is TestBaseWorkflow {
|
|
|
148
148
|
);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
function
|
|
151
|
+
function test_hiddenTokensOperatorCanAllowHolderToHideOwnTokens() public {
|
|
152
152
|
uint256 userTokens = _payUserIntoRevnet(10e18);
|
|
153
153
|
uint256 hiddenCount = userTokens / 2;
|
|
154
154
|
|
|
155
155
|
_grantPermission(USER, REVNET_ID, address(HIDDEN_TOKENS), JBPermissionIds.BURN_TOKENS);
|
|
156
|
-
|
|
156
|
+
_allowHolderToHide(USER);
|
|
157
157
|
|
|
158
158
|
vm.prank(USER);
|
|
159
159
|
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hiddenCount, USER);
|
|
160
160
|
|
|
161
|
-
vm.prank(
|
|
162
|
-
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hiddenCount,
|
|
161
|
+
vm.prank(USER);
|
|
162
|
+
HIDDEN_TOKENS.revealTokensOf(REVNET_ID, hiddenCount, USER);
|
|
163
163
|
|
|
164
164
|
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(USER, REVNET_ID), 0, "holder hidden balance was consumed");
|
|
165
165
|
assertEq(
|
|
166
|
-
jbController().TOKENS().totalBalanceOf(
|
|
167
|
-
|
|
168
|
-
"
|
|
166
|
+
jbController().TOKENS().totalBalanceOf(USER, REVNET_ID),
|
|
167
|
+
userTokens,
|
|
168
|
+
"holder gets their own revealed tokens back"
|
|
169
169
|
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function test_hiddenTokensPermissionedOperatorCanHideOwnTokens() public {
|
|
173
|
+
vm.deal(OPERATOR, 10e18);
|
|
174
|
+
vm.prank(OPERATOR);
|
|
175
|
+
uint256 operatorTokens = jbMultiTerminal().pay{value: 10e18}({
|
|
176
|
+
projectId: REVNET_ID,
|
|
177
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
178
|
+
amount: 10e18,
|
|
179
|
+
beneficiary: OPERATOR,
|
|
180
|
+
minReturnedTokens: 0,
|
|
181
|
+
memo: "",
|
|
182
|
+
metadata: ""
|
|
183
|
+
});
|
|
184
|
+
uint256 hiddenCount = operatorTokens / 2;
|
|
185
|
+
|
|
186
|
+
_grantPermission(OPERATOR, REVNET_ID, address(HIDDEN_TOKENS), JBPermissionIds.BURN_TOKENS);
|
|
187
|
+
_grantOperatorHidePermission(OPERATOR);
|
|
188
|
+
|
|
189
|
+
vm.prank(OPERATOR);
|
|
190
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, hiddenCount, OPERATOR);
|
|
191
|
+
|
|
192
|
+
assertEq(HIDDEN_TOKENS.hiddenBalanceOf(OPERATOR, REVNET_ID), hiddenCount, "operator hidden balance was updated");
|
|
170
193
|
assertEq(
|
|
171
|
-
jbController().TOKENS().totalBalanceOf(
|
|
172
|
-
|
|
173
|
-
"
|
|
194
|
+
jbController().TOKENS().totalBalanceOf(OPERATOR, REVNET_ID),
|
|
195
|
+
operatorTokens - hiddenCount,
|
|
196
|
+
"operator's visible balance was reduced"
|
|
174
197
|
);
|
|
175
198
|
}
|
|
176
199
|
|
|
200
|
+
function test_hiddenTokensDelegateCannotHideAnotherHoldersTokens() public {
|
|
201
|
+
uint256 userTokens = _payUserIntoRevnet(10e18);
|
|
202
|
+
|
|
203
|
+
_grantPermission(USER, REVNET_ID, address(HIDDEN_TOKENS), JBPermissionIds.BURN_TOKENS);
|
|
204
|
+
_allowHolderToHide(USER);
|
|
205
|
+
|
|
206
|
+
vm.prank(OPERATOR);
|
|
207
|
+
vm.expectRevert(
|
|
208
|
+
abi.encodeWithSelector(REVHiddenTokens.REVHiddenTokens_Unauthorized.selector, REVNET_ID, OPERATOR)
|
|
209
|
+
);
|
|
210
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, userTokens / 2, USER);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function test_hiddenTokensOperatorCanDisallowHolder() public {
|
|
214
|
+
uint256 userTokens = _payUserIntoRevnet(10e18);
|
|
215
|
+
|
|
216
|
+
_grantPermission(USER, REVNET_ID, address(HIDDEN_TOKENS), JBPermissionIds.BURN_TOKENS);
|
|
217
|
+
_allowHolderToHide(USER);
|
|
218
|
+
|
|
219
|
+
vm.prank(address(REV_DEPLOYER));
|
|
220
|
+
HIDDEN_TOKENS.setTokenHidingAllowedFor(REVNET_ID, USER, false);
|
|
221
|
+
|
|
222
|
+
vm.prank(USER);
|
|
223
|
+
vm.expectRevert(abi.encodeWithSelector(REVHiddenTokens.REVHiddenTokens_Unauthorized.selector, REVNET_ID, USER));
|
|
224
|
+
HIDDEN_TOKENS.hideTokensOf(REVNET_ID, userTokens / 2, USER);
|
|
225
|
+
}
|
|
226
|
+
|
|
177
227
|
function _grantPermission(address account, uint256 revnetId, address operator, uint8 permissionId) internal {
|
|
178
228
|
uint8[] memory permissionIds = new uint8[](1);
|
|
179
229
|
permissionIds[0] = permissionId;
|
|
@@ -200,6 +250,23 @@ contract NemesisOperatorDelegationTest is TestBaseWorkflow {
|
|
|
200
250
|
assertGt(tokenCount, 0, "payment should mint revnet tokens");
|
|
201
251
|
}
|
|
202
252
|
|
|
253
|
+
function _allowHolderToHide(address holder) internal {
|
|
254
|
+
vm.prank(address(REV_DEPLOYER));
|
|
255
|
+
HIDDEN_TOKENS.setTokenHidingAllowedFor(REVNET_ID, holder, true);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function _grantOperatorHidePermission(address operator) internal {
|
|
259
|
+
uint8[] memory permissionIds = new uint8[](1);
|
|
260
|
+
permissionIds[0] = JBPermissionIds.HIDE_TOKENS;
|
|
261
|
+
|
|
262
|
+
vm.prank(address(REV_DEPLOYER));
|
|
263
|
+
jbPermissions()
|
|
264
|
+
.setPermissionsFor(
|
|
265
|
+
address(REV_DEPLOYER),
|
|
266
|
+
JBPermissionsData({operator: operator, projectId: uint56(REVNET_ID), permissionIds: permissionIds})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
203
270
|
function _deployFeeProject() internal {
|
|
204
271
|
JBAccountingContext[] memory acc = new JBAccountingContext[](1);
|
|
205
272
|
acc[0] = JBAccountingContext({
|