@bananapus/721-hook-v6 0.0.41 → 0.0.43
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/foundry.lock +1 -7
- package/foundry.toml +1 -1
- package/package.json +20 -9
- package/script/Deploy.s.sol +2 -2
- package/src/JB721Checkpoints.sol +60 -18
- package/src/JB721CheckpointsDeployer.sol +10 -5
- package/src/JB721TiersHook.sol +4 -1
- package/src/JB721TiersHookProjectDeployer.sol +68 -30
- package/src/JB721TiersHookStore.sol +1 -4
- package/src/interfaces/IJB721Checkpoints.sol +21 -14
- package/src/interfaces/IJB721CheckpointsDeployer.sol +6 -2
- package/src/interfaces/IJB721TiersHookProjectDeployer.sol +2 -0
- package/test/utils/AccessJBLib.sol +49 -0
- package/test/utils/ForTest_JB721TiersHook.sol +246 -0
- package/test/utils/TestBaseWorkflow.sol +213 -0
- package/test/utils/UnitTestSetup.sol +805 -0
- package/.gas-snapshot +0 -152
- package/ADMINISTRATION.md +0 -87
- package/ARCHITECTURE.md +0 -98
- package/AUDIT_INSTRUCTIONS.md +0 -77
- package/RISKS.md +0 -118
- package/SKILLS.md +0 -43
- package/STYLE_GUIDE.md +0 -610
- package/USER_JOURNEYS.md +0 -121
- package/assets/findings/nana-721-hook-v6-pashov-ai-audit-report-20260330-091257.md +0 -83
- package/slither-ci.config.json +0 -10
- package/test/721HookAttacks.t.sol +0 -408
- package/test/E2E/Pay_Mint_Redeem_E2E.t.sol +0 -985
- package/test/Fork.t.sol +0 -2346
- package/test/TestAuditGaps.sol +0 -1075
- package/test/TestCheckpoints.t.sol +0 -341
- package/test/TestSafeTransferReentrancy.t.sol +0 -305
- package/test/TestVotingUnitsLifecycle.t.sol +0 -313
- package/test/audit/AuditRegressions.t.sol +0 -83
- package/test/audit/CrossCurrencySplitNoPrices.t.sol +0 -123
- package/test/audit/FreshAudit.t.sol +0 -197
- package/test/audit/FutureTierPoC.t.sol +0 -39
- package/test/audit/FutureTierRemoval.t.sol +0 -47
- package/test/audit/Pass12L18.t.sol +0 -80
- package/test/audit/PayCreditsBypassTierSplits.t.sol +0 -200
- package/test/audit/ProjectDeployerAuth.t.sol +0 -266
- package/test/audit/RepoFindings.t.sol +0 -195
- package/test/audit/ReserveActivation.t.sol +0 -87
- package/test/audit/RetroactiveReserveBeneficiaryDilution.t.sol +0 -149
- package/test/audit/SameCurrencyDecimalMismatch.t.sol +0 -249
- package/test/audit/SplitCreditsMismatch.t.sol +0 -219
- package/test/audit/SplitFailureRedistribution.t.sol +0 -143
- package/test/audit/USDTVoidReturnCompat.t.sol +0 -301
- package/test/fork/ERC20CashOutFork.t.sol +0 -633
- package/test/fork/ERC20TierSplitFork.t.sol +0 -596
- package/test/fork/IssueTokensForSplitsFork.t.sol +0 -516
- package/test/invariants/TierLifecycleInvariant.t.sol +0 -188
- package/test/invariants/TieredHookStoreInvariant.t.sol +0 -86
- package/test/invariants/handlers/TierLifecycleHandler.sol +0 -300
- package/test/invariants/handlers/TierStoreHandler.sol +0 -165
- package/test/regression/BrokenTerminalDoesNotDos.t.sol +0 -277
- package/test/regression/CacheTierLookup.t.sol +0 -190
- package/test/regression/ProjectDeployerRulesets.t.sol +0 -358
- package/test/regression/ReserveBeneficiaryOverwrite.t.sol +0 -155
- package/test/regression/SplitDistributionBugs.t.sol +0 -751
- package/test/regression/SplitNoBeneficiary.t.sol +0 -140
- package/test/unit/AuditFixes_Unit.t.sol +0 -624
- package/test/unit/JB721CheckpointsDeployer_AccessControl.t.sol +0 -116
- package/test/unit/JB721TiersRulesetMetadataResolver.t.sol +0 -144
- package/test/unit/JBBitmap.t.sol +0 -170
- package/test/unit/JBIpfsDecoder.t.sol +0 -136
- package/test/unit/TierSupplyReserveCheck.t.sol +0 -221
- package/test/unit/adjustTier_Unit.t.sol +0 -1942
- package/test/unit/deployer_Unit.t.sol +0 -114
- package/test/unit/getters_constructor_Unit.t.sol +0 -593
- package/test/unit/mintFor_mintReservesFor_Unit.t.sol +0 -452
- package/test/unit/pay_CrossCurrency_Unit.t.sol +0 -530
- package/test/unit/pay_Unit.t.sol +0 -1661
- package/test/unit/redeem_Unit.t.sol +0 -473
- package/test/unit/relayBeneficiary_Unit.t.sol +0 -182
- package/test/unit/splitHookDistribution_Unit.t.sol +0 -604
- package/test/unit/tierSplitRouting_Unit.t.sol +0 -757
|
@@ -1,516 +0,0 @@
|
|
|
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
|
-
|
|
7
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
8
|
-
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
9
|
-
|
|
10
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
11
|
-
import "@bananapus/core-v6/src/JBController.sol";
|
|
12
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
13
|
-
import "@bananapus/core-v6/src/JBDirectory.sol";
|
|
14
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
15
|
-
import "@bananapus/core-v6/src/JBMultiTerminal.sol";
|
|
16
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
17
|
-
import "@bananapus/core-v6/src/JBFundAccessLimits.sol";
|
|
18
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
19
|
-
import "@bananapus/core-v6/src/JBFeelessAddresses.sol";
|
|
20
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
21
|
-
import "@bananapus/core-v6/src/JBTerminalStore.sol";
|
|
22
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
23
|
-
import "@bananapus/core-v6/src/JBRulesets.sol";
|
|
24
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
25
|
-
import "@bananapus/core-v6/src/JBPermissions.sol";
|
|
26
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
27
|
-
import "@bananapus/core-v6/src/JBPrices.sol";
|
|
28
|
-
import {JBProjects} from "@bananapus/core-v6/src/JBProjects.sol";
|
|
29
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
30
|
-
import "@bananapus/core-v6/src/JBSplits.sol";
|
|
31
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
32
|
-
import "@bananapus/core-v6/src/JBERC20.sol";
|
|
33
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
34
|
-
import "@bananapus/core-v6/src/JBTokens.sol";
|
|
35
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
36
|
-
import "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
37
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
38
|
-
import "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
|
|
39
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
40
|
-
import "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
41
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
42
|
-
import "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
43
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
44
|
-
import "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
45
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
46
|
-
import "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
|
|
47
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
48
|
-
import "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
49
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
50
|
-
import "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
51
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
52
|
-
import "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
|
|
53
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
54
|
-
import "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
55
|
-
import {MetadataResolverHelper} from "@bananapus/core-v6/test/helpers/MetadataResolverHelper.sol";
|
|
56
|
-
import {JBMetadataResolver} from "@bananapus/core-v6/src/libraries/JBMetadataResolver.sol";
|
|
57
|
-
// forge-lint: disable-next-line(unused-import)
|
|
58
|
-
import {mulDiv} from "@prb/math/src/Common.sol";
|
|
59
|
-
|
|
60
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
61
|
-
import "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
62
|
-
|
|
63
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
64
|
-
import "../../src/JB721TiersHook.sol";
|
|
65
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
66
|
-
import "../../src/JB721TiersHookDeployer.sol";
|
|
67
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
68
|
-
import "../../src/JB721TiersHookProjectDeployer.sol";
|
|
69
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
70
|
-
import "../../src/JB721TiersHookStore.sol";
|
|
71
|
-
import {JB721CheckpointsDeployer} from "../../src/JB721CheckpointsDeployer.sol";
|
|
72
|
-
import {IJB721CheckpointsDeployer} from "../../src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
73
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
74
|
-
import "../../src/interfaces/IJB721TiersHook.sol";
|
|
75
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
76
|
-
import "../../src/structs/JBDeploy721TiersHookConfig.sol";
|
|
77
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
78
|
-
import "../../src/structs/JBLaunchProjectConfig.sol";
|
|
79
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
80
|
-
import "../../src/structs/JBPayDataHookRulesetConfig.sol";
|
|
81
|
-
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
82
|
-
import "../../src/structs/JBPayDataHookRulesetMetadata.sol";
|
|
83
|
-
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
84
|
-
|
|
85
|
-
/// @title IssueTokensForSplitsFork
|
|
86
|
-
/// @notice Fork tests for the issueTokensForSplits flag in JB721TiersHookFlags.
|
|
87
|
-
/// @dev When `issueTokensForSplits = true`, the payer gets tokens at full weight regardless of split routing.
|
|
88
|
-
/// When `false`, weight is scaled by `(amountValue - totalSplitAmount) / amountValue`.
|
|
89
|
-
/// @dev Run with: forge test --match-contract IssueTokensForSplitsFork -vvv --fork-url $RPC
|
|
90
|
-
contract IssueTokensForSplitsFork is Test {
|
|
91
|
-
using JBRulesetMetadataResolver for JBRuleset;
|
|
92
|
-
|
|
93
|
-
// =========================================================================
|
|
94
|
-
// Constants
|
|
95
|
-
// =========================================================================
|
|
96
|
-
|
|
97
|
-
address constant NATIVE_TOKEN = JBConstants.NATIVE_TOKEN;
|
|
98
|
-
|
|
99
|
-
// =========================================================================
|
|
100
|
-
// Actors
|
|
101
|
-
// =========================================================================
|
|
102
|
-
|
|
103
|
-
address multisig = address(0xBEEF);
|
|
104
|
-
address payer = makeAddr("payer");
|
|
105
|
-
address beneficiary = makeAddr("beneficiary");
|
|
106
|
-
address reserveBeneficiary = makeAddr("reserveBeneficiary");
|
|
107
|
-
address splitBeneficiary = makeAddr("splitBeneficiary");
|
|
108
|
-
|
|
109
|
-
// =========================================================================
|
|
110
|
-
// JB Core
|
|
111
|
-
// =========================================================================
|
|
112
|
-
|
|
113
|
-
JBPermissions jbPermissions;
|
|
114
|
-
JBProjects jbProjects;
|
|
115
|
-
JBDirectory jbDirectory;
|
|
116
|
-
JBRulesets jbRulesets;
|
|
117
|
-
JBTokens jbTokens;
|
|
118
|
-
JBSplits jbSplits;
|
|
119
|
-
JBFundAccessLimits jbFundAccessLimits;
|
|
120
|
-
JBFeelessAddresses jbFeelessAddresses;
|
|
121
|
-
JBPrices jbPrices;
|
|
122
|
-
JBController jbController;
|
|
123
|
-
JBTerminalStore jbTerminalStore;
|
|
124
|
-
JBMultiTerminal jbMultiTerminal;
|
|
125
|
-
|
|
126
|
-
// =========================================================================
|
|
127
|
-
// 721 Hook
|
|
128
|
-
// =========================================================================
|
|
129
|
-
|
|
130
|
-
JB721TiersHookStore store;
|
|
131
|
-
JB721TiersHook hookImpl;
|
|
132
|
-
JB721TiersHookDeployer hookDeployer;
|
|
133
|
-
JB721TiersHookProjectDeployer projectDeployer;
|
|
134
|
-
MetadataResolverHelper metadataHelper;
|
|
135
|
-
JBAddressRegistry addressRegistry;
|
|
136
|
-
|
|
137
|
-
// =========================================================================
|
|
138
|
-
// Setup
|
|
139
|
-
// =========================================================================
|
|
140
|
-
|
|
141
|
-
receive() external payable {}
|
|
142
|
-
|
|
143
|
-
function setUp() public {
|
|
144
|
-
vm.createSelectFork("ethereum");
|
|
145
|
-
|
|
146
|
-
_deployJBCore();
|
|
147
|
-
_deploy721Hook();
|
|
148
|
-
|
|
149
|
-
vm.deal(payer, 1000 ether);
|
|
150
|
-
vm.deal(beneficiary, 100 ether);
|
|
151
|
-
vm.deal(multisig, 100 ether);
|
|
152
|
-
|
|
153
|
-
vm.label(multisig, "multisig");
|
|
154
|
-
vm.label(payer, "payer");
|
|
155
|
-
vm.label(beneficiary, "beneficiary");
|
|
156
|
-
vm.label(reserveBeneficiary, "reserveBeneficiary");
|
|
157
|
-
vm.label(splitBeneficiary, "splitBeneficiary");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// forge-lint: disable-next-line(mixed-case-function)
|
|
161
|
-
function _deployJBCore() internal {
|
|
162
|
-
jbPermissions = new JBPermissions(address(0));
|
|
163
|
-
jbProjects = new JBProjects(multisig, address(0), address(0));
|
|
164
|
-
jbDirectory = new JBDirectory(jbPermissions, jbProjects, multisig);
|
|
165
|
-
JBERC20 jbErc20 = new JBERC20(jbPermissions, jbProjects);
|
|
166
|
-
jbTokens = new JBTokens(jbDirectory, jbErc20);
|
|
167
|
-
jbRulesets = new JBRulesets(jbDirectory);
|
|
168
|
-
jbPrices = new JBPrices(jbDirectory, jbPermissions, jbProjects, multisig, address(0));
|
|
169
|
-
jbSplits = new JBSplits(jbDirectory);
|
|
170
|
-
jbFundAccessLimits = new JBFundAccessLimits(jbDirectory);
|
|
171
|
-
jbFeelessAddresses = new JBFeelessAddresses(multisig);
|
|
172
|
-
|
|
173
|
-
jbController = new JBController(
|
|
174
|
-
jbDirectory,
|
|
175
|
-
jbFundAccessLimits,
|
|
176
|
-
jbPermissions,
|
|
177
|
-
jbPrices,
|
|
178
|
-
jbProjects,
|
|
179
|
-
jbRulesets,
|
|
180
|
-
jbSplits,
|
|
181
|
-
jbTokens,
|
|
182
|
-
address(0),
|
|
183
|
-
address(0)
|
|
184
|
-
);
|
|
185
|
-
|
|
186
|
-
vm.prank(multisig);
|
|
187
|
-
jbDirectory.setIsAllowedToSetFirstController(address(jbController), true);
|
|
188
|
-
|
|
189
|
-
jbTerminalStore = new JBTerminalStore(jbDirectory, jbPrices, jbRulesets);
|
|
190
|
-
|
|
191
|
-
jbMultiTerminal = new JBMultiTerminal(
|
|
192
|
-
jbFeelessAddresses,
|
|
193
|
-
jbPermissions,
|
|
194
|
-
jbProjects,
|
|
195
|
-
jbSplits,
|
|
196
|
-
jbTerminalStore,
|
|
197
|
-
jbTokens,
|
|
198
|
-
IPermit2(address(0)),
|
|
199
|
-
address(0)
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
vm.label(address(jbPermissions), "JBPermissions");
|
|
203
|
-
vm.label(address(jbProjects), "JBProjects");
|
|
204
|
-
vm.label(address(jbDirectory), "JBDirectory");
|
|
205
|
-
vm.label(address(jbController), "JBController");
|
|
206
|
-
vm.label(address(jbMultiTerminal), "JBMultiTerminal");
|
|
207
|
-
vm.label(address(jbTerminalStore), "JBTerminalStore");
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function _deploy721Hook() internal {
|
|
211
|
-
store = new JB721TiersHookStore();
|
|
212
|
-
hookImpl = new JB721TiersHook(
|
|
213
|
-
jbDirectory,
|
|
214
|
-
jbPermissions,
|
|
215
|
-
jbPrices,
|
|
216
|
-
jbRulesets,
|
|
217
|
-
store,
|
|
218
|
-
IJBSplits(address(jbSplits)),
|
|
219
|
-
IJB721CheckpointsDeployer(address(new JB721CheckpointsDeployer())),
|
|
220
|
-
address(0)
|
|
221
|
-
);
|
|
222
|
-
addressRegistry = new JBAddressRegistry();
|
|
223
|
-
hookDeployer = new JB721TiersHookDeployer(hookImpl, store, addressRegistry, address(0));
|
|
224
|
-
projectDeployer = new JB721TiersHookProjectDeployer(
|
|
225
|
-
IJBDirectory(jbDirectory), IJBPermissions(jbPermissions), hookDeployer, address(0)
|
|
226
|
-
);
|
|
227
|
-
metadataHelper = new MetadataResolverHelper();
|
|
228
|
-
|
|
229
|
-
vm.label(address(store), "JB721TiersHookStore");
|
|
230
|
-
vm.label(address(hookImpl), "JB721TiersHook_impl");
|
|
231
|
-
vm.label(address(projectDeployer), "JB721TiersHookProjectDeployer");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// =========================================================================
|
|
235
|
-
// Launch Helper
|
|
236
|
-
// =========================================================================
|
|
237
|
-
|
|
238
|
-
/// @dev Launch an ETH-denominated project with configurable issueTokensForSplits flag.
|
|
239
|
-
// forge-lint: disable-next-line(mixed-case-function)
|
|
240
|
-
function _launchProjectWithFlag(
|
|
241
|
-
JB721TierConfig[] memory tierConfigs,
|
|
242
|
-
bool issueTokensForSplits
|
|
243
|
-
)
|
|
244
|
-
internal
|
|
245
|
-
returns (uint256 projectId, address dataHook)
|
|
246
|
-
{
|
|
247
|
-
JBDeploy721TiersHookConfig memory hookConfig = JBDeploy721TiersHookConfig({
|
|
248
|
-
name: "TestNFT",
|
|
249
|
-
symbol: "TNFT",
|
|
250
|
-
baseUri: "ipfs://base/",
|
|
251
|
-
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
252
|
-
contractUri: "ipfs://contract",
|
|
253
|
-
tiersConfig: JB721InitTiersConfig({
|
|
254
|
-
tiers: tierConfigs,
|
|
255
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
256
|
-
currency: uint32(uint160(NATIVE_TOKEN)),
|
|
257
|
-
decimals: 18
|
|
258
|
-
}),
|
|
259
|
-
flags: JB721TiersHookFlags({
|
|
260
|
-
preventOverspending: false,
|
|
261
|
-
issueTokensForSplits: issueTokensForSplits,
|
|
262
|
-
noNewTiersWithReserves: false,
|
|
263
|
-
noNewTiersWithVotes: false,
|
|
264
|
-
noNewTiersWithOwnerMinting: false
|
|
265
|
-
})
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
JBPayDataHookRulesetMetadata memory rulesetMetadata = JBPayDataHookRulesetMetadata({
|
|
269
|
-
reservedPercent: 0,
|
|
270
|
-
cashOutTaxRate: 0,
|
|
271
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
272
|
-
baseCurrency: uint32(uint160(NATIVE_TOKEN)),
|
|
273
|
-
pausePay: false,
|
|
274
|
-
pauseCreditTransfers: false,
|
|
275
|
-
allowOwnerMinting: true,
|
|
276
|
-
allowSetCustomToken: false,
|
|
277
|
-
allowTerminalMigration: false,
|
|
278
|
-
allowSetTerminals: false,
|
|
279
|
-
allowSetController: false,
|
|
280
|
-
allowAddAccountingContext: false,
|
|
281
|
-
allowAddPriceFeed: false,
|
|
282
|
-
ownerMustSendPayouts: false,
|
|
283
|
-
holdFees: false,
|
|
284
|
-
useTotalSurplusForCashOuts: false,
|
|
285
|
-
useDataHookForCashOut: true,
|
|
286
|
-
metadata: 0x00
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
JBPayDataHookRulesetConfig[] memory rulesetConfigs = new JBPayDataHookRulesetConfig[](1);
|
|
290
|
-
rulesetConfigs[0].mustStartAtOrAfter = 0;
|
|
291
|
-
rulesetConfigs[0].duration = 0;
|
|
292
|
-
rulesetConfigs[0].weight = 1_000_000e18; // 1M tokens per ETH
|
|
293
|
-
rulesetConfigs[0].weightCutPercent = 0;
|
|
294
|
-
rulesetConfigs[0].approvalHook = IJBRulesetApprovalHook(address(0));
|
|
295
|
-
rulesetConfigs[0].metadata = rulesetMetadata;
|
|
296
|
-
|
|
297
|
-
JBAccountingContext[] memory accountingContexts = new JBAccountingContext[](1);
|
|
298
|
-
accountingContexts[0] =
|
|
299
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
300
|
-
JBAccountingContext({token: NATIVE_TOKEN, currency: uint32(uint160(NATIVE_TOKEN)), decimals: 18});
|
|
301
|
-
|
|
302
|
-
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
|
|
303
|
-
terminalConfigs[0] =
|
|
304
|
-
JBTerminalConfig({terminal: jbMultiTerminal, accountingContextsToAccept: accountingContexts});
|
|
305
|
-
|
|
306
|
-
JBLaunchProjectConfig memory launchConfig = JBLaunchProjectConfig({
|
|
307
|
-
projectUri: "test-issue-tokens-splits",
|
|
308
|
-
rulesetConfigurations: rulesetConfigs,
|
|
309
|
-
terminalConfigurations: terminalConfigs,
|
|
310
|
-
memo: ""
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
IJB721TiersHook hookInstance;
|
|
314
|
-
(projectId, hookInstance) =
|
|
315
|
-
projectDeployer.launchProjectFor(multisig, hookConfig, launchConfig, jbController, bytes32(0));
|
|
316
|
-
|
|
317
|
-
dataHook = address(hookInstance);
|
|
318
|
-
vm.label(dataHook, "hook_clone");
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// =========================================================================
|
|
322
|
-
// Metadata & Token ID Helpers
|
|
323
|
-
// =========================================================================
|
|
324
|
-
|
|
325
|
-
function _buildPayMetadata(uint16[] memory tierIds, bool allowOverspending) internal view returns (bytes memory) {
|
|
326
|
-
bytes[] memory data = new bytes[](1);
|
|
327
|
-
data[0] = abi.encode(allowOverspending, tierIds);
|
|
328
|
-
bytes4[] memory ids = new bytes4[](1);
|
|
329
|
-
ids[0] = JBMetadataResolver.getId("pay", address(hookImpl));
|
|
330
|
-
return metadataHelper.createMetadata(ids, data);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function _tokenId(uint256 tierId, uint256 mintNumber) internal pure returns (uint256) {
|
|
334
|
-
return tierId * 1_000_000_000 + mintNumber;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// =========================================================================
|
|
338
|
-
// Helper: create a tier with 50% split to splitBeneficiary
|
|
339
|
-
// =========================================================================
|
|
340
|
-
|
|
341
|
-
function _makeTierWithSplit(uint104 price, uint32 splitPercent) internal view returns (JB721TierConfig memory) {
|
|
342
|
-
JBSplit[] memory splits = new JBSplit[](1);
|
|
343
|
-
splits[0] = JBSplit({
|
|
344
|
-
percent: uint32(JBConstants.SPLITS_TOTAL_PERCENT),
|
|
345
|
-
projectId: 0,
|
|
346
|
-
beneficiary: payable(splitBeneficiary),
|
|
347
|
-
preferAddToBalance: false,
|
|
348
|
-
lockedUntil: 0,
|
|
349
|
-
hook: IJBSplitHook(address(0))
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
return JB721TierConfig({
|
|
353
|
-
price: price,
|
|
354
|
-
initialSupply: 100,
|
|
355
|
-
votingUnits: 0,
|
|
356
|
-
reserveFrequency: 0,
|
|
357
|
-
reserveBeneficiary: address(0),
|
|
358
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
359
|
-
encodedIPFSUri: bytes32("tier1"),
|
|
360
|
-
category: 1,
|
|
361
|
-
discountPercent: 0,
|
|
362
|
-
flags: JB721TierConfigFlags({
|
|
363
|
-
allowOwnerMint: false,
|
|
364
|
-
useReserveBeneficiaryAsDefault: false,
|
|
365
|
-
transfersPausable: false,
|
|
366
|
-
useVotingUnits: false,
|
|
367
|
-
cantBeRemoved: false,
|
|
368
|
-
cantIncreaseDiscountPercent: false,
|
|
369
|
-
cantBuyWithCredits: false
|
|
370
|
-
}),
|
|
371
|
-
splitPercent: splitPercent,
|
|
372
|
-
splits: splits
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// =========================================================================
|
|
377
|
-
// Test 1: issueTokensForSplits = true -> payer gets tokens at full weight
|
|
378
|
-
// =========================================================================
|
|
379
|
-
|
|
380
|
-
/// @notice When issueTokensForSplits = true and tier has splits, payer gets tokens on full amount.
|
|
381
|
-
function testFork_IssueTokensForSplitsTrueFullWeight() public {
|
|
382
|
-
// Tier: 1 ETH, 50% split to splitBeneficiary.
|
|
383
|
-
JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
|
|
384
|
-
tierConfigs[0] = _makeTierWithSplit(1 ether, 500_000_000); // 50% split
|
|
385
|
-
|
|
386
|
-
(uint256 projectId,) = _launchProjectWithFlag(tierConfigs, true);
|
|
387
|
-
|
|
388
|
-
// Pay 1 ETH to mint tier 1 NFT.
|
|
389
|
-
uint16[] memory tierIds = new uint16[](1);
|
|
390
|
-
tierIds[0] = 1;
|
|
391
|
-
bytes memory meta = _buildPayMetadata(tierIds, true);
|
|
392
|
-
|
|
393
|
-
vm.prank(payer);
|
|
394
|
-
uint256 tokenCount = jbMultiTerminal.pay{value: 1 ether}({
|
|
395
|
-
projectId: projectId,
|
|
396
|
-
amount: 1 ether,
|
|
397
|
-
token: NATIVE_TOKEN,
|
|
398
|
-
beneficiary: beneficiary,
|
|
399
|
-
minReturnedTokens: 0,
|
|
400
|
-
memo: "",
|
|
401
|
-
metadata: meta
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
// With issueTokensForSplits = true, weight = contextWeight (full).
|
|
405
|
-
// weight = 1_000_000e18, payment = 1 ETH => tokens = 1_000_000e18 * 1e18 / 1e18 = 1_000_000e18
|
|
406
|
-
// tokenCount is the number of tokens minted to the beneficiary.
|
|
407
|
-
uint256 balance = jbTokens.totalBalanceOf(beneficiary, projectId);
|
|
408
|
-
|
|
409
|
-
// Full weight: beneficiary should receive tokens based on the full 1 ETH amount.
|
|
410
|
-
// With weight = 1_000_000e18 and payment = 1 ETH: expected = 1_000_000e18.
|
|
411
|
-
assertEq(balance, 1_000_000e18, "issueTokensForSplits=true: full weight tokens minted");
|
|
412
|
-
assertEq(tokenCount, 1_000_000e18, "pay() return value should match full token count");
|
|
413
|
-
|
|
414
|
-
// Split beneficiary should have received 0.5 ETH.
|
|
415
|
-
assertGt(splitBeneficiary.balance, 0, "split beneficiary should have received ETH");
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// =========================================================================
|
|
419
|
-
// Test 2: issueTokensForSplits = false -> payer gets scaled tokens
|
|
420
|
-
// =========================================================================
|
|
421
|
-
|
|
422
|
-
/// @notice When issueTokensForSplits = false and tier has splits, payer gets scaled tokens.
|
|
423
|
-
function testFork_IssueTokensForSplitsFalseScaledWeight() public {
|
|
424
|
-
// Tier: 1 ETH, 50% split to splitBeneficiary.
|
|
425
|
-
JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
|
|
426
|
-
tierConfigs[0] = _makeTierWithSplit(1 ether, 500_000_000); // 50% split
|
|
427
|
-
|
|
428
|
-
(uint256 projectId,) = _launchProjectWithFlag(tierConfigs, false);
|
|
429
|
-
|
|
430
|
-
// Pay 1 ETH to mint tier 1 NFT.
|
|
431
|
-
uint16[] memory tierIds = new uint16[](1);
|
|
432
|
-
tierIds[0] = 1;
|
|
433
|
-
bytes memory meta = _buildPayMetadata(tierIds, true);
|
|
434
|
-
|
|
435
|
-
vm.prank(payer);
|
|
436
|
-
uint256 tokenCount = jbMultiTerminal.pay{value: 1 ether}({
|
|
437
|
-
projectId: projectId,
|
|
438
|
-
amount: 1 ether,
|
|
439
|
-
token: NATIVE_TOKEN,
|
|
440
|
-
beneficiary: beneficiary,
|
|
441
|
-
minReturnedTokens: 0,
|
|
442
|
-
memo: "",
|
|
443
|
-
metadata: meta
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
// With issueTokensForSplits = false, weight = contextWeight * (amount - splitAmount) / amount.
|
|
447
|
-
// splitAmount = 50% of 1 ETH = 0.5 ETH.
|
|
448
|
-
// weight = 1_000_000e18 * (1e18 - 0.5e18) / 1e18 = 500_000e18.
|
|
449
|
-
// tokens = 500_000e18 * 1e18 / 1e18 = 500_000e18.
|
|
450
|
-
uint256 balance = jbTokens.totalBalanceOf(beneficiary, projectId);
|
|
451
|
-
|
|
452
|
-
// Scaled weight: beneficiary should receive tokens based on 50% of the payment.
|
|
453
|
-
assertEq(balance, 500_000e18, "issueTokensForSplits=false: scaled weight tokens minted");
|
|
454
|
-
assertEq(tokenCount, 500_000e18, "pay() return value should match scaled token count");
|
|
455
|
-
|
|
456
|
-
// Split beneficiary should have received 0.5 ETH.
|
|
457
|
-
assertGt(splitBeneficiary.balance, 0, "split beneficiary should have received ETH");
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// =========================================================================
|
|
461
|
-
// Test 3: 100% to splits edge case
|
|
462
|
-
// =========================================================================
|
|
463
|
-
|
|
464
|
-
/// @notice 100% to splits: flag true -> tokens still minted at full weight; flag false -> weight = 0, no tokens.
|
|
465
|
-
function testFork_IssueTokensForSplitsEdgeCaseAllSplits() public {
|
|
466
|
-
// Tier: 1 ETH, 100% split to splitBeneficiary (SPLITS_TOTAL_PERCENT = 1e9).
|
|
467
|
-
JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
|
|
468
|
-
tierConfigs[0] = _makeTierWithSplit(1 ether, 1_000_000_000); // 100% split
|
|
469
|
-
|
|
470
|
-
// --- Part A: issueTokensForSplits = true ---
|
|
471
|
-
(uint256 projectIdTrue,) = _launchProjectWithFlag(tierConfigs, true);
|
|
472
|
-
|
|
473
|
-
uint16[] memory tierIds = new uint16[](1);
|
|
474
|
-
tierIds[0] = 1;
|
|
475
|
-
bytes memory meta = _buildPayMetadata(tierIds, true);
|
|
476
|
-
|
|
477
|
-
vm.prank(payer);
|
|
478
|
-
uint256 tokenCountTrue = jbMultiTerminal.pay{value: 1 ether}({
|
|
479
|
-
projectId: projectIdTrue,
|
|
480
|
-
amount: 1 ether,
|
|
481
|
-
token: NATIVE_TOKEN,
|
|
482
|
-
beneficiary: beneficiary,
|
|
483
|
-
minReturnedTokens: 0,
|
|
484
|
-
memo: "",
|
|
485
|
-
metadata: meta
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
uint256 balanceTrue = jbTokens.totalBalanceOf(beneficiary, projectIdTrue);
|
|
489
|
-
|
|
490
|
-
// issueTokensForSplits=true: full weight even though 100% goes to splits.
|
|
491
|
-
assertEq(balanceTrue, 1_000_000e18, "100% splits + flag=true: full weight tokens minted");
|
|
492
|
-
assertEq(tokenCountTrue, 1_000_000e18, "pay() return value: full tokens with flag=true");
|
|
493
|
-
|
|
494
|
-
// --- Part B: issueTokensForSplits = false ---
|
|
495
|
-
(uint256 projectIdFalse,) = _launchProjectWithFlag(tierConfigs, false);
|
|
496
|
-
|
|
497
|
-
meta = _buildPayMetadata(tierIds, true);
|
|
498
|
-
|
|
499
|
-
vm.prank(payer);
|
|
500
|
-
uint256 tokenCountFalse = jbMultiTerminal.pay{value: 1 ether}({
|
|
501
|
-
projectId: projectIdFalse,
|
|
502
|
-
amount: 1 ether,
|
|
503
|
-
token: NATIVE_TOKEN,
|
|
504
|
-
beneficiary: beneficiary,
|
|
505
|
-
minReturnedTokens: 0,
|
|
506
|
-
memo: "",
|
|
507
|
-
metadata: meta
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
uint256 balanceFalse = jbTokens.totalBalanceOf(beneficiary, projectIdFalse);
|
|
511
|
-
|
|
512
|
-
// issueTokensForSplits=false + 100% splits: weight = 0, no tokens minted.
|
|
513
|
-
assertEq(balanceFalse, 0, "100% splits + flag=false: zero tokens minted");
|
|
514
|
-
assertEq(tokenCountFalse, 0, "pay() return value: zero tokens with flag=false");
|
|
515
|
-
}
|
|
516
|
-
}
|