@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
|
@@ -0,0 +1,805 @@
|
|
|
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 "../utils/ForTest_JB721TiersHook.sol";
|
|
8
|
+
|
|
9
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
10
|
+
import "../../src/JB721TiersHookDeployer.sol";
|
|
11
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
12
|
+
import "../../src/JB721TiersHook.sol";
|
|
13
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
14
|
+
import "../../src/abstract/JB721Hook.sol";
|
|
15
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
16
|
+
import "../../src/JB721TiersHookStore.sol";
|
|
17
|
+
|
|
18
|
+
import {JB721CheckpointsDeployer} from "../../src/JB721CheckpointsDeployer.sol";
|
|
19
|
+
import {IJB721CheckpointsDeployer} from "../../src/interfaces/IJB721CheckpointsDeployer.sol";
|
|
20
|
+
|
|
21
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
22
|
+
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
|
|
23
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
24
|
+
import "@bananapus/core-v6/src/libraries/JBRulesetMetadataResolver.sol";
|
|
25
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
26
|
+
import "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
27
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
28
|
+
import "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
|
|
29
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
30
|
+
import "@bananapus/core-v6/src/structs/JBAfterPayRecordedContext.sol";
|
|
31
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
32
|
+
import "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
|
|
33
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
34
|
+
import "@bananapus/core-v6/src/structs/JBAfterCashOutRecordedContext.sol";
|
|
35
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
36
|
+
import "@bananapus/core-v6/src/structs/JBCashOutHookSpecification.sol";
|
|
37
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
38
|
+
import "@bananapus/core-v6/src/structs/JBFundAccessLimitGroup.sol";
|
|
39
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
40
|
+
import "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
41
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
42
|
+
import "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
43
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
44
|
+
import "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
45
|
+
|
|
46
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
47
|
+
import "../../src/structs/JBLaunchProjectConfig.sol";
|
|
48
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
49
|
+
import "../../src/structs/JBPayDataHookRulesetMetadata.sol";
|
|
50
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
51
|
+
import {JB721TierFlags} from "../../src/structs/JB721TierFlags.sol";
|
|
52
|
+
|
|
53
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
54
|
+
import "@bananapus/address-registry-v6/src/JBAddressRegistry.sol";
|
|
55
|
+
|
|
56
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
57
|
+
import "@bananapus/core-v6/src/libraries/JBCurrencyIds.sol";
|
|
58
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
59
|
+
import "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
60
|
+
// forge-lint: disable-next-line(unaliased-plain-import)
|
|
61
|
+
import "@bananapus/core-v6/src/structs/JBBeforeCashOutRecordedContext.sol";
|
|
62
|
+
import {MetadataResolverHelper} from "@bananapus/core-v6/test/helpers/MetadataResolverHelper.sol";
|
|
63
|
+
|
|
64
|
+
contract UnitTestSetup is Test {
|
|
65
|
+
address beneficiary;
|
|
66
|
+
address owner;
|
|
67
|
+
address reserveBeneficiary;
|
|
68
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
69
|
+
address mockJBController;
|
|
70
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
71
|
+
address mockJBDirectory;
|
|
72
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
73
|
+
address mockJBPrices;
|
|
74
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
75
|
+
address mockJBRulesets;
|
|
76
|
+
address mockTokenUriResolver;
|
|
77
|
+
address mockTerminalAddress;
|
|
78
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
79
|
+
address mockJBProjects;
|
|
80
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
81
|
+
address mockJBPermissions;
|
|
82
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
83
|
+
address mockJBSplits;
|
|
84
|
+
|
|
85
|
+
string name = "NAME";
|
|
86
|
+
string symbol = "SYM";
|
|
87
|
+
string baseUri = "http://www.null.com/";
|
|
88
|
+
string contractUri = "ipfs://null";
|
|
89
|
+
string rulesetMemo = "meemoo";
|
|
90
|
+
|
|
91
|
+
uint256 projectId = 69;
|
|
92
|
+
|
|
93
|
+
uint256 constant SURPLUS = 10e18;
|
|
94
|
+
|
|
95
|
+
uint256 constant CASH_OUT_TAX_RATE = 6000; // 60%
|
|
96
|
+
|
|
97
|
+
// forge-lint: disable-next-line(screaming-snake-case-const)
|
|
98
|
+
address constant trustedForwarder = address(123_456);
|
|
99
|
+
|
|
100
|
+
JB721TierConfig defaultTierConfig;
|
|
101
|
+
|
|
102
|
+
// To generate:
|
|
103
|
+
// NodeJS: function con(hash) { Buffer.from(bs58.decode(hash).slice(2)).toString('hex') }
|
|
104
|
+
// JavaScript: 0x${bs58.decode(hash).slice(2).toString('hex')})
|
|
105
|
+
bytes32[] tokenUris = [
|
|
106
|
+
bytes32(0x7D5A99F603F231D53A4F39D1521F98D2E8BB279CF29BEBFD0687DC98458E7F89),
|
|
107
|
+
bytes32(0xf5d60fc6f462f6176982833f5e2ca222a2ced265fa94e4ce1c477d74910250ed),
|
|
108
|
+
bytes32(0x4258512cfb09993d9f3613a59ffc592a5593abf3c06ed57a22656c5fbca4de23),
|
|
109
|
+
bytes32(0xae7035a8ef12433adbf4a55f2063696972bcf50434fe70ee6d8ab78f83e358c8),
|
|
110
|
+
bytes32(0xae7035a8ef12433adbf4a55f2faabecff3446276fdbc6f6209e6bba25ee358c8),
|
|
111
|
+
bytes32(0xae7035a8ef1242fc4b803a9284453843f278307462311f8b8b90fddfcbe358c8),
|
|
112
|
+
bytes32(0xae824fb9f7de128f66cb5e224e4f8c65f37c479ee6ec7193c8741d6f997f5a18),
|
|
113
|
+
bytes32(0xae7035a8f8d14617dd6d904265fe7d84a493c628385ffba7016d6463c852e8c8),
|
|
114
|
+
bytes32(0xae7035a8ef12433adbf4a55f2063696972bcf50434fe70ee6d8ab78f74adbbf7),
|
|
115
|
+
bytes32(0xae7035a8ef12433adbf4a55f2063696972bcf51c38098273db23452d955758c8)
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
string[] theoreticHashes = [
|
|
119
|
+
"QmWmyoMoctfbAaiEs2G46gpeUmhqFRDW6KWo64y5r581Vz",
|
|
120
|
+
"QmetHutWQPz3qfu5jhTi1bqbRZXt8zAJaqqxkiJoJCX9DN",
|
|
121
|
+
"QmSodj3RSrXKPy3WRFSPz8HRDVyRdrBtjxBfoiWBgtGugN",
|
|
122
|
+
"Qma5atSTeoKJkcXe2R7gdmcvPvLJJkh2jd4cDZeM1wnFgK",
|
|
123
|
+
"Qma5atSTeoKJkcXe2R7typcvPvLJJkh2jd4cDZeM1wnFgK",
|
|
124
|
+
"Qma5atSTeoKJKSQSDFcgdmcvPvLJJkh2jd4cDZeM1wnFgK",
|
|
125
|
+
"Qma5rtytgfdgzrg4345RFGdfbzert345rfgvs5YRtSTkcX",
|
|
126
|
+
"Qma5atSTkcXe2R7gdmcvPvLJJkh2234234QcDZeM1wnFgK",
|
|
127
|
+
"Qma5atSTeoKJkcXe2R7gdmcvPvLJJkh2jd4cDZeM1ZERze",
|
|
128
|
+
"Qma5atSTeoKJkcXe2R7gdmcvPvLJLkh2jd4cDZeM1wnFgK"
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
JB721TierConfig[] tiers;
|
|
132
|
+
JB721TiersHookStore store;
|
|
133
|
+
JB721TiersHook hook;
|
|
134
|
+
JB721TiersHook hookOrigin;
|
|
135
|
+
JBAddressRegistry addressRegistry;
|
|
136
|
+
JB721TiersHookDeployer jbHookDeployer;
|
|
137
|
+
MetadataResolverHelper metadataHelper;
|
|
138
|
+
|
|
139
|
+
// forge-lint: disable-next-line(mixed-case-variable)
|
|
140
|
+
address hook_i = address(bytes20(keccak256("hook_implementation")));
|
|
141
|
+
|
|
142
|
+
event Mint(
|
|
143
|
+
uint256 indexed tokenId,
|
|
144
|
+
uint256 indexed tierId,
|
|
145
|
+
address indexed beneficiary,
|
|
146
|
+
uint256 totalAmountPaid,
|
|
147
|
+
address caller
|
|
148
|
+
);
|
|
149
|
+
event MintReservedNft(uint256 indexed tokenId, uint256 indexed tierId, address indexed beneficiary, address caller);
|
|
150
|
+
event AddTier(uint256 indexed tierId, JB721TierConfig tier, address caller);
|
|
151
|
+
event RemoveTier(uint256 indexed tierId, address caller);
|
|
152
|
+
event CleanTiers(address indexed nft, address caller);
|
|
153
|
+
event AddPayCredits(
|
|
154
|
+
uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
|
|
155
|
+
);
|
|
156
|
+
event UsePayCredits(
|
|
157
|
+
uint256 indexed amount, uint256 indexed newTotalCredits, address indexed account, address caller
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
function setUp() public virtual {
|
|
161
|
+
beneficiary = makeAddr("beneficiary");
|
|
162
|
+
owner = makeAddr("owner");
|
|
163
|
+
reserveBeneficiary = makeAddr("reserveBeneficiary");
|
|
164
|
+
mockJBDirectory = makeAddr("mockJBDirectory");
|
|
165
|
+
mockJBPrices = makeAddr("mockJBPrices");
|
|
166
|
+
mockJBRulesets = makeAddr("mockJBRulesets");
|
|
167
|
+
mockTerminalAddress = makeAddr("mockTerminalAddress");
|
|
168
|
+
mockJBProjects = makeAddr("mockJBProjects");
|
|
169
|
+
mockJBPermissions = makeAddr("mockJBPermissions");
|
|
170
|
+
mockJBSplits = makeAddr("mockJBSplits");
|
|
171
|
+
mockJBController = makeAddr("mockJBController");
|
|
172
|
+
mockTokenUriResolver = address(0);
|
|
173
|
+
|
|
174
|
+
vm.etch(mockJBDirectory, new bytes(0x69));
|
|
175
|
+
vm.etch(mockJBRulesets, new bytes(0x69));
|
|
176
|
+
vm.etch(mockTokenUriResolver, new bytes(0x69));
|
|
177
|
+
vm.etch(mockTerminalAddress, new bytes(0x69));
|
|
178
|
+
vm.etch(mockJBProjects, new bytes(0x69));
|
|
179
|
+
vm.etch(mockJBController, new bytes(0x69));
|
|
180
|
+
|
|
181
|
+
// Set default tier config field-by-field (avoids nested dynamic array storage copy).
|
|
182
|
+
defaultTierConfig.reserveFrequency = uint16(10);
|
|
183
|
+
defaultTierConfig.reserveBeneficiary = reserveBeneficiary;
|
|
184
|
+
defaultTierConfig.category = type(uint24).max;
|
|
185
|
+
defaultTierConfig.flags.useVotingUnits = true;
|
|
186
|
+
|
|
187
|
+
// Create 10 tiers, each with 100 NFTs available to mint.
|
|
188
|
+
for (uint256 i; i < 10; i++) {
|
|
189
|
+
tiers.push(); // Push default-initialized struct (avoids nested dynamic array storage copy).
|
|
190
|
+
tiers[i].price = uint104((i + 1) * 10); // The price is `tierId` * 10.
|
|
191
|
+
tiers[i].initialSupply = uint32(100);
|
|
192
|
+
tiers[i].reserveBeneficiary = reserveBeneficiary;
|
|
193
|
+
tiers[i].encodedIPFSUri = tokenUris[i];
|
|
194
|
+
tiers[i].category = uint24(100);
|
|
195
|
+
tiers[i].flags.useVotingUnits = true;
|
|
196
|
+
}
|
|
197
|
+
vm.mockCall(
|
|
198
|
+
mockJBRulesets,
|
|
199
|
+
abi.encodeCall(IJBRulesets.currentOf, projectId),
|
|
200
|
+
abi.encode(
|
|
201
|
+
JBRuleset({
|
|
202
|
+
cycleNumber: 1,
|
|
203
|
+
id: uint48(block.timestamp),
|
|
204
|
+
basedOnId: 0,
|
|
205
|
+
start: uint48(block.timestamp),
|
|
206
|
+
duration: 600,
|
|
207
|
+
weight: 10e18,
|
|
208
|
+
weightCutPercent: 0,
|
|
209
|
+
approvalHook: IJBRulesetApprovalHook(address(0)),
|
|
210
|
+
metadata: JBRulesetMetadataResolver.packRulesetMetadata(
|
|
211
|
+
JBRulesetMetadata({
|
|
212
|
+
reservedPercent: 5000, //50%
|
|
213
|
+
cashOutTaxRate: 6000, //60%
|
|
214
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
215
|
+
pausePay: false,
|
|
216
|
+
pauseCreditTransfers: false,
|
|
217
|
+
allowOwnerMinting: true,
|
|
218
|
+
allowSetCustomToken: false,
|
|
219
|
+
allowTerminalMigration: false,
|
|
220
|
+
allowSetTerminals: false,
|
|
221
|
+
allowSetController: false,
|
|
222
|
+
allowAddAccountingContext: false,
|
|
223
|
+
allowAddPriceFeed: false,
|
|
224
|
+
ownerMustSendPayouts: false,
|
|
225
|
+
holdFees: false,
|
|
226
|
+
useTotalSurplusForCashOuts: false,
|
|
227
|
+
useDataHookForPay: true,
|
|
228
|
+
useDataHookForCashOut: true,
|
|
229
|
+
dataHook: address(0),
|
|
230
|
+
metadata: 0x00
|
|
231
|
+
})
|
|
232
|
+
)
|
|
233
|
+
})
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
vm.mockCall(mockJBDirectory, abi.encodeWithSelector(IJBDirectory.PROJECTS.selector), abi.encode(mockJBProjects));
|
|
238
|
+
vm.mockCall(
|
|
239
|
+
mockJBDirectory, abi.encodeWithSelector(IJBPermissioned.PERMISSIONS.selector), abi.encode(mockJBPermissions)
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
store = new JB721TiersHookStore();
|
|
243
|
+
JB721CheckpointsDeployer checkpointsDeployer = new JB721CheckpointsDeployer(store);
|
|
244
|
+
hookOrigin = new JB721TiersHook(
|
|
245
|
+
IJBDirectory(mockJBDirectory),
|
|
246
|
+
IJBPermissions(mockJBPermissions),
|
|
247
|
+
IJBPrices(mockJBPrices),
|
|
248
|
+
IJBRulesets(mockJBRulesets),
|
|
249
|
+
IJB721TiersHookStore(store),
|
|
250
|
+
IJBSplits(mockJBSplits),
|
|
251
|
+
IJB721CheckpointsDeployer(address(checkpointsDeployer)),
|
|
252
|
+
trustedForwarder
|
|
253
|
+
);
|
|
254
|
+
addressRegistry = new JBAddressRegistry();
|
|
255
|
+
jbHookDeployer = new JB721TiersHookDeployer(hookOrigin, store, addressRegistry, trustedForwarder);
|
|
256
|
+
JBDeploy721TiersHookConfig memory hookConfig = JBDeploy721TiersHookConfig({
|
|
257
|
+
name: name,
|
|
258
|
+
symbol: symbol,
|
|
259
|
+
baseUri: baseUri,
|
|
260
|
+
tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
|
|
261
|
+
contractUri: contractUri,
|
|
262
|
+
tiersConfig: JB721InitTiersConfig({
|
|
263
|
+
tiers: tiers, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
|
|
264
|
+
}),
|
|
265
|
+
flags: JB721TiersHookFlags({
|
|
266
|
+
preventOverspending: false,
|
|
267
|
+
issueTokensForSplits: false,
|
|
268
|
+
noNewTiersWithReserves: true,
|
|
269
|
+
noNewTiersWithVotes: true,
|
|
270
|
+
noNewTiersWithOwnerMinting: true
|
|
271
|
+
})
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
hook = JB721TiersHook(address(jbHookDeployer.deployHookFor(projectId, hookConfig, bytes32(0))));
|
|
275
|
+
hook.transferOwnership(owner);
|
|
276
|
+
|
|
277
|
+
metadataHelper = new MetadataResolverHelper();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
281
|
+
function USD() internal pure returns (uint256) {
|
|
282
|
+
return JBCurrencyIds.USD;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
286
|
+
function NATIVE_TOKEN() internal pure returns (address) {
|
|
287
|
+
return JBConstants.NATIVE_TOKEN;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
291
|
+
function MAX_FEE() internal pure returns (uint256) {
|
|
292
|
+
return JBConstants.MAX_FEE;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
296
|
+
function MAX_RESERVED_PERCENT() internal pure returns (uint256) {
|
|
297
|
+
return JBConstants.MAX_RESERVED_PERCENT;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
301
|
+
function MAX_CASH_OUT_TAX_RATE() internal pure returns (uint256) {
|
|
302
|
+
return JBConstants.MAX_CASH_OUT_TAX_RATE;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
306
|
+
function MAX_WEIGHT_CUT_PERCENT() internal pure returns (uint256) {
|
|
307
|
+
return JBConstants.MAX_WEIGHT_CUT_PERCENT;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
311
|
+
function SPLITS_TOTAL_PERCENT() internal pure returns (uint256) {
|
|
312
|
+
return JBConstants.SPLITS_TOTAL_PERCENT;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// *------------------*
|
|
316
|
+
// * INTERNAL HELPERS *
|
|
317
|
+
// *------------------*
|
|
318
|
+
|
|
319
|
+
// Compare two `JB721Tier`s.
|
|
320
|
+
function assertEq(JB721Tier memory first, JB721Tier memory second) internal {
|
|
321
|
+
assertEq(first.id, second.id);
|
|
322
|
+
assertEq(first.price, second.price);
|
|
323
|
+
assertEq(first.remainingSupply, second.remainingSupply);
|
|
324
|
+
assertEq(first.initialSupply, second.initialSupply);
|
|
325
|
+
assertEq(first.votingUnits, second.votingUnits);
|
|
326
|
+
assertEq(first.reserveFrequency, second.reserveFrequency);
|
|
327
|
+
assertEq(first.reserveBeneficiary, second.reserveBeneficiary);
|
|
328
|
+
assertEq(first.encodedIPFSUri, second.encodedIPFSUri);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Compare two arrays of `JB721Tier`s.
|
|
332
|
+
function assertEq(JB721Tier[] memory first, JB721Tier[] memory second) internal {
|
|
333
|
+
assertEq(first.length, second.length);
|
|
334
|
+
for (uint256 i; i < first.length; i++) {
|
|
335
|
+
assertEq(first[i].id, second[i].id);
|
|
336
|
+
assertEq(first[i].price, second[i].price);
|
|
337
|
+
assertEq(first[i].remainingSupply, second[i].remainingSupply);
|
|
338
|
+
assertEq(first[i].initialSupply, second[i].initialSupply);
|
|
339
|
+
assertEq(first[i].votingUnits, second[i].votingUnits);
|
|
340
|
+
assertEq(first[i].reserveFrequency, second[i].reserveFrequency);
|
|
341
|
+
assertEq(first[i].reserveBeneficiary, second[i].reserveBeneficiary);
|
|
342
|
+
assertEq(first[i].encodedIPFSUri, second[i].encodedIPFSUri);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function mockAndExpect(address target, bytes memory mockCalldata, bytes memory returnData) internal {
|
|
347
|
+
vm.mockCall(target, mockCalldata, returnData);
|
|
348
|
+
vm.expectCall(target, mockCalldata);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Generate `tokenId`s based on a `tierId` and a `tokenNumber` within the tier.
|
|
352
|
+
function _generateTokenId(uint256 tierId, uint256 tokenNumber) internal pure returns (uint256) {
|
|
353
|
+
return (tierId * 1_000_000_000) + tokenNumber;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Check if every elements from smol is in bigg.
|
|
357
|
+
function _isIn(JB721Tier[] memory smol, JB721Tier[] memory bigg) internal returns (bool) {
|
|
358
|
+
// smol cannot be bigger than bigg.
|
|
359
|
+
if (smol.length > bigg.length) {
|
|
360
|
+
emit log("_isIn: smol too big");
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
if (smol.length == 0) return true;
|
|
364
|
+
uint256 count;
|
|
365
|
+
// Iterate on every smol element.
|
|
366
|
+
for (uint256 smolIter; smolIter < smol.length; smolIter++) {
|
|
367
|
+
// Compare it with every bigg element until...
|
|
368
|
+
for (uint256 biggIter; biggIter < bigg.length; biggIter++) {
|
|
369
|
+
// ... the same element is found, then break to go to the next smol element.
|
|
370
|
+
if (_compareTiers(smol[smolIter], bigg[biggIter])) {
|
|
371
|
+
count += smolIter + 1; // 1-indexed, as the length.
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Ensure that all the smol indexes have been iterated on (i.e. we've seen `sum(1, 2, ..., smol.length)`
|
|
377
|
+
// elements).
|
|
378
|
+
if (count == (smol.length * (smol.length + 1)) / 2) {
|
|
379
|
+
return true;
|
|
380
|
+
} else {
|
|
381
|
+
emit log("_isIn: incomplete inclusion");
|
|
382
|
+
emit log_uint(count);
|
|
383
|
+
emit log_uint(smol.length);
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function _compareTiers(JB721Tier memory first, JB721Tier memory second) internal pure returns (bool) {
|
|
389
|
+
return (first.id == second.id && first.price == second.price && first.remainingSupply == second.remainingSupply
|
|
390
|
+
&& first.initialSupply == second.initialSupply && first.votingUnits == second.votingUnits
|
|
391
|
+
&& first.reserveFrequency == second.reserveFrequency
|
|
392
|
+
&& first.reserveBeneficiary == second.reserveBeneficiary
|
|
393
|
+
&& first.encodedIPFSUri == second.encodedIPFSUri
|
|
394
|
+
&& keccak256(abi.encodePacked(first.resolvedUri)) == keccak256(abi.encodePacked(second.resolvedUri)));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Simple selection sort for an array of `uint256` values.
|
|
398
|
+
function _sortArray(uint256[] memory arr) internal pure returns (uint256[] memory) {
|
|
399
|
+
for (uint256 i; i < arr.length; i++) {
|
|
400
|
+
uint256 minIndex = i;
|
|
401
|
+
uint256 minValue = arr[i];
|
|
402
|
+
for (uint256 j = i; j < arr.length; j++) {
|
|
403
|
+
if (arr[j] < minValue) {
|
|
404
|
+
minIndex = j;
|
|
405
|
+
minValue = arr[j];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (minIndex != i) (arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);
|
|
409
|
+
}
|
|
410
|
+
return arr;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Simple selection sort for an array of `uint16` values.
|
|
414
|
+
function _sortArray(uint16[] memory arr) internal pure returns (uint16[] memory) {
|
|
415
|
+
for (uint256 i; i < arr.length; i++) {
|
|
416
|
+
uint256 minIndex = i;
|
|
417
|
+
uint16 minValue = arr[i];
|
|
418
|
+
for (uint256 j = i; j < arr.length; j++) {
|
|
419
|
+
if (arr[j] < minValue) {
|
|
420
|
+
minIndex = j;
|
|
421
|
+
minValue = arr[j];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (minIndex != i) (arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);
|
|
425
|
+
}
|
|
426
|
+
return arr;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Simple selection sort for an array of `uint8` values.
|
|
430
|
+
function _sortArray(uint8[] memory arr) internal pure returns (uint8[] memory) {
|
|
431
|
+
for (uint256 i; i < arr.length; i++) {
|
|
432
|
+
uint256 minIndex = i;
|
|
433
|
+
uint8 minValue = arr[i];
|
|
434
|
+
for (uint256 j = i; j < arr.length; j++) {
|
|
435
|
+
if (arr[j] < minValue) {
|
|
436
|
+
minIndex = j;
|
|
437
|
+
minValue = arr[j];
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if (minIndex != i) (arr[i], arr[minIndex]) = (arr[minIndex], arr[i]);
|
|
441
|
+
}
|
|
442
|
+
return arr;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Create a random array of `uint16` values based on a given `seed`.
|
|
446
|
+
function _createArray(uint256 length, uint256 seed) internal pure returns (uint16[] memory) {
|
|
447
|
+
uint16[] memory out = new uint16[](length);
|
|
448
|
+
|
|
449
|
+
for (uint256 i; i < length; i++) {
|
|
450
|
+
out[i] = uint16(uint256(keccak256(abi.encode(seed, i))));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Create an array of `JB721TierConfig`s and `JB721Tier`s using the provided and the default `prices`, `initialId`,
|
|
457
|
+
// and `categoryIncrement`.
|
|
458
|
+
function _createTiers(
|
|
459
|
+
JB721TierConfig memory tierConfig,
|
|
460
|
+
uint256 numberOfTiers
|
|
461
|
+
)
|
|
462
|
+
internal
|
|
463
|
+
view
|
|
464
|
+
returns (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory newTiers)
|
|
465
|
+
{
|
|
466
|
+
return _createTiers(tierConfig, numberOfTiers, 0, new uint16[](numberOfTiers), 0);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Create an array of `JB721TierConfig`s and `JB721Tier`s using the provided and the default `prices` and
|
|
470
|
+
// `initialId`.
|
|
471
|
+
function _createTiers(
|
|
472
|
+
JB721TierConfig memory tierConfig,
|
|
473
|
+
uint256 numberOfTiers,
|
|
474
|
+
uint256 categoryIncrement
|
|
475
|
+
)
|
|
476
|
+
internal
|
|
477
|
+
view
|
|
478
|
+
returns (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory newTiers)
|
|
479
|
+
{
|
|
480
|
+
return _createTiers(tierConfig, numberOfTiers, 0, new uint16[](numberOfTiers), categoryIncrement);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Create an array of `JB721TierConfig`s and `JB721Tier`s using the provided and the default `categoryIncrement`.
|
|
484
|
+
function _createTiers(
|
|
485
|
+
JB721TierConfig memory tierConfig,
|
|
486
|
+
uint256 numberOfTiers,
|
|
487
|
+
uint256 initialId,
|
|
488
|
+
uint16[] memory prices
|
|
489
|
+
)
|
|
490
|
+
internal
|
|
491
|
+
view
|
|
492
|
+
returns (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory newTiers)
|
|
493
|
+
{
|
|
494
|
+
return _createTiers(tierConfig, numberOfTiers, initialId, prices, 0);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Create an array of `JB721TierConfig`s and `JB721Tier`s using the provided parameters.
|
|
498
|
+
function _createTiers(
|
|
499
|
+
JB721TierConfig memory tierConfig,
|
|
500
|
+
uint256 numberOfTiers,
|
|
501
|
+
uint256 initialId,
|
|
502
|
+
uint16[] memory prices,
|
|
503
|
+
uint256 categoryIncrement
|
|
504
|
+
)
|
|
505
|
+
internal
|
|
506
|
+
view
|
|
507
|
+
returns (JB721TierConfig[] memory tierConfigs, JB721Tier[] memory newTiers)
|
|
508
|
+
{
|
|
509
|
+
tierConfigs = new JB721TierConfig[](numberOfTiers);
|
|
510
|
+
newTiers = new JB721Tier[](numberOfTiers);
|
|
511
|
+
|
|
512
|
+
for (uint256 i; i < numberOfTiers; i++) {
|
|
513
|
+
tierConfigs[i] = JB721TierConfig({
|
|
514
|
+
price: prices[i] == 0 ? uint16((i + 1) * 10) : prices[i], // The price is `tierId` * 10.
|
|
515
|
+
initialSupply: tierConfig.initialSupply == 0 ? uint32(100) : tierConfig.initialSupply,
|
|
516
|
+
votingUnits: tierConfig.votingUnits,
|
|
517
|
+
reserveFrequency: tierConfig.reserveFrequency,
|
|
518
|
+
reserveBeneficiary: reserveBeneficiary,
|
|
519
|
+
encodedIPFSUri: i < tokenUris.length ? tokenUris[i] : tokenUris[0],
|
|
520
|
+
category: categoryIncrement == 0
|
|
521
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
522
|
+
? tierConfig.category == type(uint24).max ? uint24(i * 2 + 1) : tierConfig.category
|
|
523
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
524
|
+
: uint24(i * 2 + categoryIncrement),
|
|
525
|
+
discountPercent: tierConfig.discountPercent,
|
|
526
|
+
flags: JB721TierConfigFlags({
|
|
527
|
+
allowOwnerMint: tierConfig.flags.allowOwnerMint,
|
|
528
|
+
useReserveBeneficiaryAsDefault: tierConfig.flags.useReserveBeneficiaryAsDefault,
|
|
529
|
+
transfersPausable: tierConfig.flags.transfersPausable,
|
|
530
|
+
useVotingUnits: tierConfig.flags.useVotingUnits,
|
|
531
|
+
cantBeRemoved: tierConfig.flags.cantBeRemoved,
|
|
532
|
+
cantIncreaseDiscountPercent: tierConfig.flags.cantIncreaseDiscountPercent,
|
|
533
|
+
cantBuyWithCredits: tierConfig.flags.cantBuyWithCredits
|
|
534
|
+
}),
|
|
535
|
+
splitPercent: tierConfig.splitPercent,
|
|
536
|
+
splits: new JBSplit[](0)
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
newTiers[i] = JB721Tier({
|
|
540
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
541
|
+
id: uint32(initialId + i + 1),
|
|
542
|
+
price: tierConfigs[i].price,
|
|
543
|
+
remainingSupply: tierConfigs[i].initialSupply,
|
|
544
|
+
initialSupply: tierConfigs[i].initialSupply,
|
|
545
|
+
votingUnits: tierConfigs[i].votingUnits,
|
|
546
|
+
reserveFrequency: tierConfigs[i].reserveFrequency,
|
|
547
|
+
reserveBeneficiary: tierConfigs[i].reserveBeneficiary,
|
|
548
|
+
encodedIPFSUri: tierConfigs[i].encodedIPFSUri,
|
|
549
|
+
category: tierConfigs[i].category,
|
|
550
|
+
discountPercent: tierConfigs[i].discountPercent,
|
|
551
|
+
flags: JB721TierFlags({
|
|
552
|
+
allowOwnerMint: tierConfigs[i].flags.allowOwnerMint,
|
|
553
|
+
transfersPausable: tierConfigs[i].flags.transfersPausable,
|
|
554
|
+
cantBeRemoved: tierConfigs[i].flags.cantBeRemoved,
|
|
555
|
+
cantIncreaseDiscountPercent: tierConfigs[i].flags.cantIncreaseDiscountPercent,
|
|
556
|
+
cantBuyWithCredits: tierConfigs[i].flags.cantBuyWithCredits
|
|
557
|
+
}),
|
|
558
|
+
splitPercent: tierConfigs[i].splitPercent,
|
|
559
|
+
resolvedUri: defaultTierConfig.encodedIPFSUri == bytes32(0)
|
|
560
|
+
? ""
|
|
561
|
+
: string(abi.encodePacked("resolverURI", _generateTokenId(initialId + i + 1, 0)))
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Add the specified tiers to the hook, and remove the specified number of tiers from the hook. Uses `adjustTiers`.
|
|
567
|
+
function _addDeleteTiers(
|
|
568
|
+
JB721TiersHook tiersHook,
|
|
569
|
+
uint256 currentNumberOfTiers,
|
|
570
|
+
uint256 numberOfTiersToRemove,
|
|
571
|
+
JB721TierConfig[] memory tiersToAdd
|
|
572
|
+
)
|
|
573
|
+
internal
|
|
574
|
+
returns (uint256)
|
|
575
|
+
{
|
|
576
|
+
uint256 newNumberOfTiers = currentNumberOfTiers;
|
|
577
|
+
// Cap removals to the number of tiers that actually exist.
|
|
578
|
+
uint256 actualRemovals =
|
|
579
|
+
numberOfTiersToRemove > currentNumberOfTiers ? currentNumberOfTiers : numberOfTiersToRemove;
|
|
580
|
+
uint256[] memory tiersToRemove = new uint256[](actualRemovals);
|
|
581
|
+
|
|
582
|
+
for (uint256 i; i < actualRemovals; i++) {
|
|
583
|
+
tiersToRemove[i] = currentNumberOfTiers - i;
|
|
584
|
+
newNumberOfTiers--;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
newNumberOfTiers += tiersToAdd.length;
|
|
588
|
+
|
|
589
|
+
vm.startPrank(owner);
|
|
590
|
+
tiersHook.adjustTiers(tiersToAdd, tiersToRemove);
|
|
591
|
+
tiersHook.STORE().cleanTiers(address(tiersHook));
|
|
592
|
+
vm.stopPrank();
|
|
593
|
+
|
|
594
|
+
JB721Tier[] memory storedTiers =
|
|
595
|
+
tiersHook.STORE().tiersOf(address(tiersHook), new uint256[](0), false, 0, newNumberOfTiers);
|
|
596
|
+
assertEq(storedTiers.length, newNumberOfTiers);
|
|
597
|
+
|
|
598
|
+
return newNumberOfTiers;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Initialize a hook with tiers that use the default tier config.
|
|
602
|
+
// Use default pricing context (native token, 18 decimals, and 0 address as oracle).
|
|
603
|
+
// Don't prevent overspending.
|
|
604
|
+
function _initHookDefaultTiers(uint256 initialNumberOfTiers) internal returns (JB721TiersHook) {
|
|
605
|
+
return _initHookDefaultTiers(initialNumberOfTiers, false, uint32(uint160(JBConstants.NATIVE_TOKEN)), 18);
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Initialize a hook with tiers that use the default tier config.
|
|
609
|
+
// Use default pricing context (native token, 18 decimals).
|
|
610
|
+
function _initHookDefaultTiers(
|
|
611
|
+
uint256 initialNumberOfTiers,
|
|
612
|
+
bool preventOverspending
|
|
613
|
+
)
|
|
614
|
+
internal
|
|
615
|
+
returns (JB721TiersHook)
|
|
616
|
+
{
|
|
617
|
+
return
|
|
618
|
+
_initHookDefaultTiers(
|
|
619
|
+
initialNumberOfTiers, preventOverspending, uint32(uint160(JBConstants.NATIVE_TOKEN)), 18
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Initialize a hook with tiers that use the default tier config.
|
|
624
|
+
function _initHookDefaultTiers(
|
|
625
|
+
uint256 initialNumberOfTiers,
|
|
626
|
+
bool preventOverspending,
|
|
627
|
+
uint32 currency,
|
|
628
|
+
uint8 decimals
|
|
629
|
+
)
|
|
630
|
+
internal
|
|
631
|
+
returns (JB721TiersHook tiersHook)
|
|
632
|
+
{
|
|
633
|
+
// Initialize first tiers to add.
|
|
634
|
+
(JB721TierConfig[] memory tierConfigs,) = _createTiers(defaultTierConfig, initialNumberOfTiers);
|
|
635
|
+
|
|
636
|
+
// Deploy the hook.
|
|
637
|
+
vm.etch(hook_i, address(hook).code);
|
|
638
|
+
tiersHook = JB721TiersHook(hook_i);
|
|
639
|
+
|
|
640
|
+
// Initialize the hook's flags and init config in memory (for stack's sake).
|
|
641
|
+
JB721TiersHookFlags memory flags = JB721TiersHookFlags({
|
|
642
|
+
preventOverspending: preventOverspending,
|
|
643
|
+
issueTokensForSplits: false,
|
|
644
|
+
noNewTiersWithReserves: false,
|
|
645
|
+
noNewTiersWithVotes: false,
|
|
646
|
+
noNewTiersWithOwnerMinting: false
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
JB721InitTiersConfig memory initConfig =
|
|
650
|
+
JB721InitTiersConfig({tiers: tierConfigs, currency: currency, decimals: decimals});
|
|
651
|
+
|
|
652
|
+
tiersHook.initialize(
|
|
653
|
+
projectId,
|
|
654
|
+
name,
|
|
655
|
+
symbol,
|
|
656
|
+
baseUri,
|
|
657
|
+
IJB721TokenUriResolver(mockTokenUriResolver),
|
|
658
|
+
contractUri,
|
|
659
|
+
initConfig,
|
|
660
|
+
flags
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
// Transfer ownership to owner.
|
|
664
|
+
tiersHook.transferOwnership(owner);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Initialize a 721 tiers hook specialized for testing purposes.
|
|
668
|
+
function _initializeForTestHook(uint256 initialNumberOfTiers) internal returns (ForTest_JB721TiersHook tiersHook) {
|
|
669
|
+
// Initialize first tiers to add.
|
|
670
|
+
(JB721TierConfig[] memory tierConfigs,) = _createTiers(defaultTierConfig, initialNumberOfTiers);
|
|
671
|
+
|
|
672
|
+
// Deploy the ForTest hook store.
|
|
673
|
+
ForTest_JB721TiersHookStore hookStore = new ForTest_JB721TiersHookStore();
|
|
674
|
+
|
|
675
|
+
// Deploy the ForTest hook.
|
|
676
|
+
tiersHook = new ForTest_JB721TiersHook(
|
|
677
|
+
ForTest_JB721TiersHook.ForTestInitConfig({
|
|
678
|
+
projectId: projectId,
|
|
679
|
+
name: name,
|
|
680
|
+
symbol: symbol,
|
|
681
|
+
baseUri: baseUri,
|
|
682
|
+
tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
|
|
683
|
+
contractUri: contractUri,
|
|
684
|
+
tiers: tierConfigs,
|
|
685
|
+
flags: JB721TiersHookFlags({
|
|
686
|
+
preventOverspending: false,
|
|
687
|
+
issueTokensForSplits: false,
|
|
688
|
+
noNewTiersWithReserves: false,
|
|
689
|
+
noNewTiersWithVotes: false,
|
|
690
|
+
noNewTiersWithOwnerMinting: true
|
|
691
|
+
})
|
|
692
|
+
}),
|
|
693
|
+
IJBDirectory(mockJBDirectory),
|
|
694
|
+
IJBPrices(mockJBPrices),
|
|
695
|
+
IJBRulesets(mockJBRulesets),
|
|
696
|
+
IJB721TiersHookStore(address(hookStore)),
|
|
697
|
+
IJBSplits(mockJBSplits)
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
// Transfer the hook's ownership to owner.
|
|
701
|
+
tiersHook.transferOwnership(owner);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Create a default `JBDeploy712TiersHookConfig` and `JBLaunchProjectConfig` to quickly bootstrap a 721 tiers hook
|
|
705
|
+
// and project.
|
|
706
|
+
function createData()
|
|
707
|
+
internal
|
|
708
|
+
view
|
|
709
|
+
returns (JBDeploy721TiersHookConfig memory tiersHookConfig, JBLaunchProjectConfig memory launchProjectConfig)
|
|
710
|
+
{
|
|
711
|
+
string memory projectUri;
|
|
712
|
+
JBPayDataHookRulesetConfig[] memory rulesetConfigurations;
|
|
713
|
+
JBTerminalConfig[] memory terminalConfigurations;
|
|
714
|
+
JBPayDataHookRulesetMetadata memory metadata;
|
|
715
|
+
JBFundAccessLimitGroup[] memory fundAccessLimitGroups;
|
|
716
|
+
JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](10);
|
|
717
|
+
for (uint256 i; i < 10; i++) {
|
|
718
|
+
tierConfigs[i] = JB721TierConfig({
|
|
719
|
+
price: uint104((i + 1) * 10),
|
|
720
|
+
initialSupply: uint32(100),
|
|
721
|
+
votingUnits: uint16(0),
|
|
722
|
+
reserveFrequency: uint16(0),
|
|
723
|
+
reserveBeneficiary: reserveBeneficiary,
|
|
724
|
+
encodedIPFSUri: tokenUris[i],
|
|
725
|
+
category: uint24(100),
|
|
726
|
+
discountPercent: uint8(0),
|
|
727
|
+
flags: JB721TierConfigFlags({
|
|
728
|
+
allowOwnerMint: false,
|
|
729
|
+
useReserveBeneficiaryAsDefault: false,
|
|
730
|
+
transfersPausable: false,
|
|
731
|
+
useVotingUnits: true,
|
|
732
|
+
cantBeRemoved: false,
|
|
733
|
+
cantIncreaseDiscountPercent: false,
|
|
734
|
+
cantBuyWithCredits: false
|
|
735
|
+
}),
|
|
736
|
+
splitPercent: 0,
|
|
737
|
+
splits: new JBSplit[](0)
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
tiersHookConfig = JBDeploy721TiersHookConfig({
|
|
741
|
+
name: name,
|
|
742
|
+
symbol: symbol,
|
|
743
|
+
baseUri: baseUri,
|
|
744
|
+
tokenUriResolver: IJB721TokenUriResolver(mockTokenUriResolver),
|
|
745
|
+
contractUri: contractUri,
|
|
746
|
+
tiersConfig: JB721InitTiersConfig({
|
|
747
|
+
tiers: tierConfigs, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
|
|
748
|
+
}),
|
|
749
|
+
flags: JB721TiersHookFlags({
|
|
750
|
+
preventOverspending: false,
|
|
751
|
+
issueTokensForSplits: false,
|
|
752
|
+
noNewTiersWithReserves: true,
|
|
753
|
+
noNewTiersWithVotes: true,
|
|
754
|
+
noNewTiersWithOwnerMinting: true
|
|
755
|
+
})
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
projectUri = "myIPFSHash";
|
|
759
|
+
|
|
760
|
+
metadata = JBPayDataHookRulesetMetadata({
|
|
761
|
+
reservedPercent: 5000, //50%
|
|
762
|
+
cashOutTaxRate: 5000, //50%
|
|
763
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
764
|
+
pausePay: false,
|
|
765
|
+
pauseCreditTransfers: false,
|
|
766
|
+
allowOwnerMinting: false,
|
|
767
|
+
allowSetCustomToken: false,
|
|
768
|
+
allowTerminalMigration: false,
|
|
769
|
+
allowSetTerminals: false,
|
|
770
|
+
allowSetController: false,
|
|
771
|
+
ownerMustSendPayouts: false,
|
|
772
|
+
allowAddAccountingContext: false,
|
|
773
|
+
allowAddPriceFeed: false,
|
|
774
|
+
holdFees: false,
|
|
775
|
+
useTotalSurplusForCashOuts: false,
|
|
776
|
+
useDataHookForCashOut: false,
|
|
777
|
+
metadata: 0x00
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
rulesetConfigurations = new JBPayDataHookRulesetConfig[](1);
|
|
781
|
+
rulesetConfigurations[0].mustStartAtOrAfter = 0;
|
|
782
|
+
rulesetConfigurations[0].duration = 14;
|
|
783
|
+
rulesetConfigurations[0].weight = 10 ** 18;
|
|
784
|
+
rulesetConfigurations[0].weightCutPercent = 450_000_000;
|
|
785
|
+
rulesetConfigurations[0].approvalHook = IJBRulesetApprovalHook(address(0));
|
|
786
|
+
rulesetConfigurations[0].metadata = metadata;
|
|
787
|
+
rulesetConfigurations[0].fundAccessLimitGroups = fundAccessLimitGroups;
|
|
788
|
+
|
|
789
|
+
terminalConfigurations = new JBTerminalConfig[](1);
|
|
790
|
+
JBAccountingContext[] memory accountingContextsToAccept = new JBAccountingContext[](1);
|
|
791
|
+
accountingContextsToAccept[0] = JBAccountingContext({
|
|
792
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18, token: JBConstants.NATIVE_TOKEN
|
|
793
|
+
});
|
|
794
|
+
terminalConfigurations[0] = JBTerminalConfig({
|
|
795
|
+
terminal: IJBTerminal(mockTerminalAddress), accountingContextsToAccept: accountingContextsToAccept
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
launchProjectConfig = JBLaunchProjectConfig({
|
|
799
|
+
projectUri: projectUri,
|
|
800
|
+
rulesetConfigurations: rulesetConfigurations,
|
|
801
|
+
terminalConfigurations: terminalConfigurations,
|
|
802
|
+
memo: rulesetMemo
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|