@bananapus/721-hook-v6 0.0.37 → 0.0.39
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 +12 -0
- package/CHANGELOG.md +4 -0
- package/package.json +5 -5
- package/src/JB721TiersHookProjectDeployer.sol +3 -3
- package/test/audit/20260425CodexNemesisFutureTierRemoval.t.sol +56 -0
- package/test/audit/20260425CodexNemesisReserveActivation.t.sol +93 -0
- package/test/audit/CodexNemesisFreshAudit.t.sol +272 -0
- package/test/audit/CodexNemesisFutureTierPoC.t.sol +51 -0
- package/test/audit/CodexNemesisProjectDeployerAuth.t.sol +273 -0
package/ADMINISTRATION.md
CHANGED
|
@@ -36,6 +36,18 @@
|
|
|
36
36
|
- `setMetadata(...)`
|
|
37
37
|
- deployer setup and hook ownership transfer paths
|
|
38
38
|
|
|
39
|
+
## Deployer Permission Model
|
|
40
|
+
|
|
41
|
+
`JB721TiersHookProjectDeployer` requires callers to hold the correct Juicebox permission for each operation:
|
|
42
|
+
|
|
43
|
+
| Function | Required Permissions |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `launchProjectFor(...)` | None (creates a new project) |
|
|
46
|
+
| `launchRulesetsFor(...)` | `LAUNCH_RULESETS` + `SET_TERMINALS` |
|
|
47
|
+
| `queueRulesetsOf(...)` | `QUEUE_RULESETS` |
|
|
48
|
+
|
|
49
|
+
Permissions are checked against the project owner via `_requirePermissionFrom`. The deployer calls the controller on the caller's behalf, so the controller sees the deployer as `msg.sender`.
|
|
50
|
+
|
|
39
51
|
## Immutable And One-Way
|
|
40
52
|
|
|
41
53
|
- many tier properties are immutable once created
|
package/CHANGELOG.md
CHANGED
|
@@ -20,6 +20,10 @@ This file describes the verified change from `nana-721-hook-v5` to the current `
|
|
|
20
20
|
- The repo now carries a dedicated helper library to keep the hook surface manageable and to support the larger v6 feature set.
|
|
21
21
|
- The repo was upgraded from the v5 Solidity baseline to `0.8.28`.
|
|
22
22
|
|
|
23
|
+
## Local audit remediations
|
|
24
|
+
|
|
25
|
+
- `JB721TiersHookProjectDeployer.launchRulesetsFor` now checks `LAUNCH_RULESETS` instead of `QUEUE_RULESETS`. The previous check was semantically wrong — launching active rulesets should require the launch permission, not the queue permission.
|
|
26
|
+
|
|
23
27
|
## Verified deltas
|
|
24
28
|
|
|
25
29
|
- `IJB721TiersHook.pricingContext()` changed from a three-value return to `(currency, decimals)`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/721-hook-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-721-hook-v6'"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bananapus/address-registry-v6": "^0.0.
|
|
21
|
-
"@bananapus/core-v6": "^0.0.
|
|
22
|
-
"@bananapus/ownable-v6": "^0.0.
|
|
23
|
-
"@bananapus/permission-ids-v6": "^0.0.
|
|
20
|
+
"@bananapus/address-registry-v6": "^0.0.20",
|
|
21
|
+
"@bananapus/core-v6": "^0.0.36",
|
|
22
|
+
"@bananapus/ownable-v6": "^0.0.20",
|
|
23
|
+
"@bananapus/permission-ids-v6": "^0.0.19",
|
|
24
24
|
"@openzeppelin/contracts": "^5.6.1",
|
|
25
25
|
"@prb/math": "^4.1.0",
|
|
26
26
|
"solady": "^0.1.8"
|
|
@@ -24,7 +24,7 @@ import {JBQueueRulesetsConfig} from "./structs/JBQueueRulesetsConfig.sol";
|
|
|
24
24
|
|
|
25
25
|
/// @title JB721TiersHookProjectDeployer
|
|
26
26
|
/// @notice Deploys a project and a 721 tiers hook for it. Can be used to queue rulesets for the project if given
|
|
27
|
-
/// `JBPermissionIds.QUEUE_RULESETS`.
|
|
27
|
+
/// `JBPermissionIds.QUEUE_RULESETS` or `JBPermissionIds.LAUNCH_RULESETS`.
|
|
28
28
|
contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721TiersHookProjectDeployer {
|
|
29
29
|
//*********************************************************************//
|
|
30
30
|
// --------------- public immutable stored properties ---------------- //
|
|
@@ -102,7 +102,7 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/// @notice Launches rulesets for a project with an attached 721 tiers hook.
|
|
105
|
-
/// @dev Only a project's owner or an operator with the `
|
|
105
|
+
/// @dev Only a project's owner or an operator with the `LAUNCH_RULESETS & SET_TERMINALS` permission can launch its
|
|
106
106
|
/// rulesets.
|
|
107
107
|
/// @param projectId The ID of the project that rulesets are being launched for.
|
|
108
108
|
/// @param deployTiersHookConfig Configuration which dictates the behavior of the 721 tiers hook which is being
|
|
@@ -128,7 +128,7 @@ contract JB721TiersHookProjectDeployer is ERC2771Context, JBPermissioned, IJB721
|
|
|
128
128
|
|
|
129
129
|
// Enforce permissions.
|
|
130
130
|
_requirePermissionFrom({
|
|
131
|
-
account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.
|
|
131
|
+
account: PROJECTS.ownerOf(projectId), projectId: projectId, permissionId: JBPermissionIds.LAUNCH_RULESETS
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
_requirePermissionFrom({
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
6
|
+
|
|
7
|
+
import {JB721TiersHookStore} from "../../src/JB721TiersHookStore.sol";
|
|
8
|
+
import {JB721TierConfig} from "../../src/structs/JB721TierConfig.sol";
|
|
9
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
10
|
+
|
|
11
|
+
contract Test_20260425CodexNemesisFutureTierRemoval is Test {
|
|
12
|
+
JB721TiersHookStore internal store;
|
|
13
|
+
|
|
14
|
+
function setUp() external {
|
|
15
|
+
store = new JB721TiersHookStore();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function test_futureRemovedTierIdIsBornRemovedAndCannotMint() external {
|
|
19
|
+
JB721TierConfig[] memory firstTier = new JB721TierConfig[](1);
|
|
20
|
+
firstTier[0] = _tier(1);
|
|
21
|
+
uint256[] memory firstIds = store.recordAddTiers(firstTier);
|
|
22
|
+
assertEq(firstIds[0], 1);
|
|
23
|
+
|
|
24
|
+
uint256[] memory futureIds = new uint256[](1);
|
|
25
|
+
futureIds[0] = 2;
|
|
26
|
+
store.recordRemoveTierIds(futureIds);
|
|
27
|
+
assertTrue(store.isTierRemoved({hook: address(this), tierId: 2}));
|
|
28
|
+
|
|
29
|
+
JB721TierConfig[] memory secondTier = new JB721TierConfig[](1);
|
|
30
|
+
secondTier[0] = _tier(2);
|
|
31
|
+
uint256[] memory secondIds = store.recordAddTiers(secondTier);
|
|
32
|
+
assertEq(secondIds[0], 2);
|
|
33
|
+
assertTrue(store.isTierRemoved({hook: address(this), tierId: 2}));
|
|
34
|
+
|
|
35
|
+
uint16[] memory tierIds = new uint16[](1);
|
|
36
|
+
tierIds[0] = 2;
|
|
37
|
+
vm.expectRevert(abi.encodeWithSignature("JB721TiersHookStore_TierRemoved(uint256)", 2));
|
|
38
|
+
store.recordMint({amount: 1, tierIds: tierIds, isOwnerMint: false});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _tier(uint24 category) internal pure returns (JB721TierConfig memory tier) {
|
|
42
|
+
tier.price = 1;
|
|
43
|
+
tier.initialSupply = 10;
|
|
44
|
+
tier.category = category;
|
|
45
|
+
tier.flags = JB721TierConfigFlags({
|
|
46
|
+
allowOwnerMint: false,
|
|
47
|
+
useReserveBeneficiaryAsDefault: false,
|
|
48
|
+
transfersPausable: false,
|
|
49
|
+
useVotingUnits: false,
|
|
50
|
+
cantBeRemoved: false,
|
|
51
|
+
cantIncreaseDiscountPercent: false,
|
|
52
|
+
cantBuyWithCredits: false
|
|
53
|
+
});
|
|
54
|
+
tier.splits = new JBSplit[](0);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
6
|
+
|
|
7
|
+
import {JB721TiersHookStore} from "../../src/JB721TiersHookStore.sol";
|
|
8
|
+
import {JB721Tier} from "../../src/structs/JB721Tier.sol";
|
|
9
|
+
import {JB721TierConfig} from "../../src/structs/JB721TierConfig.sol";
|
|
10
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
11
|
+
|
|
12
|
+
contract Test_20260425CodexNemesisReserveActivation is Test {
|
|
13
|
+
JB721TiersHookStore internal store;
|
|
14
|
+
|
|
15
|
+
function setUp() external {
|
|
16
|
+
store = new JB721TiersHookStore();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function test_retroactiveDefaultReserveBeneficiaryCreatesUnmintablePendingReserves() external {
|
|
20
|
+
JB721TierConfig[] memory initialTiers = new JB721TierConfig[](1);
|
|
21
|
+
initialTiers[0] = _tier({
|
|
22
|
+
price: 1,
|
|
23
|
+
initialSupply: 10,
|
|
24
|
+
reserveFrequency: 2,
|
|
25
|
+
reserveBeneficiary: address(0),
|
|
26
|
+
useReserveBeneficiaryAsDefault: false,
|
|
27
|
+
category: 1
|
|
28
|
+
});
|
|
29
|
+
store.recordAddTiers(initialTiers);
|
|
30
|
+
|
|
31
|
+
uint16[] memory tierIds = new uint16[](10);
|
|
32
|
+
for (uint256 i; i < tierIds.length; i++) {
|
|
33
|
+
tierIds[i] = 1;
|
|
34
|
+
}
|
|
35
|
+
store.recordMint({amount: 10, tierIds: tierIds, isOwnerMint: false});
|
|
36
|
+
|
|
37
|
+
JB721Tier memory beforeDefault = store.tierOf({hook: address(this), id: 1, includeResolvedUri: false});
|
|
38
|
+
assertEq(beforeDefault.remainingSupply, 0);
|
|
39
|
+
assertEq(beforeDefault.reserveFrequency, 0);
|
|
40
|
+
assertEq(store.numberOfPendingReservesFor({hook: address(this), tierId: 1}), 0);
|
|
41
|
+
assertEq(store.totalCashOutWeight(address(this)), 10);
|
|
42
|
+
|
|
43
|
+
JB721TierConfig[] memory laterTiers = new JB721TierConfig[](1);
|
|
44
|
+
laterTiers[0] = _tier({
|
|
45
|
+
price: 1,
|
|
46
|
+
initialSupply: 10,
|
|
47
|
+
reserveFrequency: 2,
|
|
48
|
+
reserveBeneficiary: address(0xBEEF),
|
|
49
|
+
useReserveBeneficiaryAsDefault: true,
|
|
50
|
+
category: 2
|
|
51
|
+
});
|
|
52
|
+
store.recordAddTiers(laterTiers);
|
|
53
|
+
|
|
54
|
+
JB721Tier memory afterDefault = store.tierOf({hook: address(this), id: 1, includeResolvedUri: false});
|
|
55
|
+
assertEq(afterDefault.remainingSupply, 0);
|
|
56
|
+
assertEq(afterDefault.reserveFrequency, 2);
|
|
57
|
+
assertEq(afterDefault.reserveBeneficiary, address(0xBEEF));
|
|
58
|
+
assertEq(store.numberOfPendingReservesFor({hook: address(this), tierId: 1}), 5);
|
|
59
|
+
assertEq(store.totalCashOutWeight(address(this)), 15);
|
|
60
|
+
|
|
61
|
+
vm.expectRevert();
|
|
62
|
+
store.recordMintReservesFor({tierId: 1, count: 1});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function _tier(
|
|
66
|
+
uint104 price,
|
|
67
|
+
uint32 initialSupply,
|
|
68
|
+
uint16 reserveFrequency,
|
|
69
|
+
address reserveBeneficiary,
|
|
70
|
+
bool useReserveBeneficiaryAsDefault,
|
|
71
|
+
uint24 category
|
|
72
|
+
)
|
|
73
|
+
internal
|
|
74
|
+
pure
|
|
75
|
+
returns (JB721TierConfig memory tier)
|
|
76
|
+
{
|
|
77
|
+
tier.price = price;
|
|
78
|
+
tier.initialSupply = initialSupply;
|
|
79
|
+
tier.reserveFrequency = reserveFrequency;
|
|
80
|
+
tier.reserveBeneficiary = reserveBeneficiary;
|
|
81
|
+
tier.category = category;
|
|
82
|
+
tier.flags = JB721TierConfigFlags({
|
|
83
|
+
allowOwnerMint: false,
|
|
84
|
+
useReserveBeneficiaryAsDefault: useReserveBeneficiaryAsDefault,
|
|
85
|
+
transfersPausable: false,
|
|
86
|
+
useVotingUnits: false,
|
|
87
|
+
cantBeRemoved: false,
|
|
88
|
+
cantIncreaseDiscountPercent: false,
|
|
89
|
+
cantBuyWithCredits: false
|
|
90
|
+
});
|
|
91
|
+
tier.splits = new JBSplit[](0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {UnitTestSetup} from "../utils/UnitTestSetup.sol";
|
|
5
|
+
import {ForTest_JB721TiersHook} from "../utils/ForTest_JB721TiersHook.sol";
|
|
6
|
+
import {IJB721TiersHookStore} from "../../src/interfaces/IJB721TiersHookStore.sol";
|
|
7
|
+
import {JB721TierConfig} from "../../src/structs/JB721TierConfig.sol";
|
|
8
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
9
|
+
import {JBBeforePayRecordedContext} from "@bananapus/core-v6/src/structs/JBBeforePayRecordedContext.sol";
|
|
10
|
+
import {JBAfterPayRecordedContext} from "@bananapus/core-v6/src/structs/JBAfterPayRecordedContext.sol";
|
|
11
|
+
import {JBPayHookSpecification} from "@bananapus/core-v6/src/structs/JBPayHookSpecification.sol";
|
|
12
|
+
import {JBTokenAmount} from "@bananapus/core-v6/src/structs/JBTokenAmount.sol";
|
|
13
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
14
|
+
import {IJBSplitHook} from "@bananapus/core-v6/src/interfaces/IJBSplitHook.sol";
|
|
15
|
+
import {IJBSplits} from "@bananapus/core-v6/src/interfaces/IJBSplits.sol";
|
|
16
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
17
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
18
|
+
|
|
19
|
+
contract CodexNemesisFreshAudit is UnitTestSetup {
|
|
20
|
+
function _buildPayMetadata(
|
|
21
|
+
address hookAddress,
|
|
22
|
+
bool allowOverspending,
|
|
23
|
+
uint16[] memory tierIdsToMint
|
|
24
|
+
)
|
|
25
|
+
internal
|
|
26
|
+
view
|
|
27
|
+
returns (bytes memory)
|
|
28
|
+
{
|
|
29
|
+
bytes[] memory data = new bytes[](1);
|
|
30
|
+
data[0] = abi.encode(allowOverspending, tierIdsToMint);
|
|
31
|
+
|
|
32
|
+
bytes4[] memory ids = new bytes4[](1);
|
|
33
|
+
ids[0] = metadataHelper.getId("pay", hookAddress);
|
|
34
|
+
|
|
35
|
+
return metadataHelper.createMetadata(ids, data);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function _nativeAmount(uint256 value) internal pure returns (JBTokenAmount memory) {
|
|
39
|
+
return JBTokenAmount({
|
|
40
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
41
|
+
value: value,
|
|
42
|
+
decimals: 18,
|
|
43
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function test_payCredits_can_underfund_split_bearing_tier_mints() public {
|
|
48
|
+
ForTest_JB721TiersHook testHook = _initializeForTestHook(0);
|
|
49
|
+
IJB721TiersHookStore hookStore = testHook.STORE();
|
|
50
|
+
address splitReceiver = makeAddr("splitReceiver");
|
|
51
|
+
|
|
52
|
+
JB721TierConfig[] memory tiersToAdd = new JB721TierConfig[](1);
|
|
53
|
+
tiersToAdd[0] = JB721TierConfig({
|
|
54
|
+
price: uint104(1 ether),
|
|
55
|
+
initialSupply: uint32(10),
|
|
56
|
+
votingUnits: 0,
|
|
57
|
+
reserveFrequency: 0,
|
|
58
|
+
reserveBeneficiary: address(0),
|
|
59
|
+
encodedIPFSUri: bytes32(uint256(1)),
|
|
60
|
+
category: uint24(1),
|
|
61
|
+
discountPercent: 0,
|
|
62
|
+
flags: JB721TierConfigFlags({
|
|
63
|
+
allowOwnerMint: false,
|
|
64
|
+
useReserveBeneficiaryAsDefault: false,
|
|
65
|
+
transfersPausable: false,
|
|
66
|
+
useVotingUnits: false,
|
|
67
|
+
cantBeRemoved: false,
|
|
68
|
+
cantIncreaseDiscountPercent: false,
|
|
69
|
+
cantBuyWithCredits: false
|
|
70
|
+
}),
|
|
71
|
+
splitPercent: uint32(JBConstants.SPLITS_TOTAL_PERCENT),
|
|
72
|
+
splits: new JBSplit[](0)
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
vm.prank(owner);
|
|
76
|
+
testHook.adjustTiers(tiersToAdd, new uint256[](0));
|
|
77
|
+
|
|
78
|
+
JBSplit[] memory splits = new JBSplit[](1);
|
|
79
|
+
splits[0] = JBSplit({
|
|
80
|
+
percent: uint32(JBConstants.SPLITS_TOTAL_PERCENT),
|
|
81
|
+
projectId: 0,
|
|
82
|
+
beneficiary: payable(splitReceiver),
|
|
83
|
+
preferAddToBalance: false,
|
|
84
|
+
lockedUntil: 0,
|
|
85
|
+
hook: IJBSplitHook(address(0))
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
uint256 groupId = uint256(uint160(address(testHook))) | (uint256(1) << 160);
|
|
89
|
+
vm.mockCall(
|
|
90
|
+
mockJBSplits, abi.encodeWithSelector(IJBSplits.splitsOf.selector, projectId, 0, groupId), abi.encode(splits)
|
|
91
|
+
);
|
|
92
|
+
vm.mockCall(
|
|
93
|
+
mockJBDirectory,
|
|
94
|
+
abi.encodeWithSelector(IJBDirectory.isTerminalOf.selector, projectId, mockTerminalAddress),
|
|
95
|
+
abi.encode(true)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
JBAfterPayRecordedContext memory seedCredits = JBAfterPayRecordedContext({
|
|
99
|
+
payer: beneficiary,
|
|
100
|
+
projectId: projectId,
|
|
101
|
+
rulesetId: 0,
|
|
102
|
+
amount: _nativeAmount(1 ether),
|
|
103
|
+
forwardedAmount: _nativeAmount(0),
|
|
104
|
+
weight: 10e18,
|
|
105
|
+
newlyIssuedTokenCount: 0,
|
|
106
|
+
beneficiary: beneficiary,
|
|
107
|
+
hookMetadata: bytes(""),
|
|
108
|
+
payerMetadata: bytes("")
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
vm.prank(mockTerminalAddress);
|
|
112
|
+
testHook.afterPayRecordedWith(seedCredits);
|
|
113
|
+
assertEq(testHook.payCreditsOf(beneficiary), 1 ether, "setup: credits should be seeded");
|
|
114
|
+
|
|
115
|
+
uint16[] memory tierIds = new uint16[](1);
|
|
116
|
+
tierIds[0] = 1;
|
|
117
|
+
bytes memory payerMetadata = _buildPayMetadata(address(testHook), true, tierIds);
|
|
118
|
+
|
|
119
|
+
JBBeforePayRecordedContext memory beforeContext = JBBeforePayRecordedContext({
|
|
120
|
+
terminal: mockTerminalAddress,
|
|
121
|
+
payer: beneficiary,
|
|
122
|
+
amount: _nativeAmount(1),
|
|
123
|
+
projectId: projectId,
|
|
124
|
+
rulesetId: 0,
|
|
125
|
+
beneficiary: beneficiary,
|
|
126
|
+
weight: 10e18,
|
|
127
|
+
reservedPercent: 0,
|
|
128
|
+
metadata: payerMetadata
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
(uint256 weight, JBPayHookSpecification[] memory hookSpecifications) =
|
|
132
|
+
testHook.beforePayRecordedWith(beforeContext);
|
|
133
|
+
|
|
134
|
+
assertEq(weight, 0, "all fresh payment value is treated as split-routed");
|
|
135
|
+
assertEq(hookSpecifications.length, 1, "expected single pay hook spec");
|
|
136
|
+
assertEq(hookSpecifications[0].amount, 1, "split forwarding is capped to the fresh payment only");
|
|
137
|
+
|
|
138
|
+
JBAfterPayRecordedContext memory mintWithCredits = JBAfterPayRecordedContext({
|
|
139
|
+
payer: beneficiary,
|
|
140
|
+
projectId: projectId,
|
|
141
|
+
rulesetId: 0,
|
|
142
|
+
amount: _nativeAmount(1),
|
|
143
|
+
forwardedAmount: _nativeAmount(hookSpecifications[0].amount),
|
|
144
|
+
weight: weight,
|
|
145
|
+
newlyIssuedTokenCount: 0,
|
|
146
|
+
beneficiary: beneficiary,
|
|
147
|
+
hookMetadata: hookSpecifications[0].metadata,
|
|
148
|
+
payerMetadata: payerMetadata
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
vm.deal(mockTerminalAddress, 1);
|
|
152
|
+
vm.prank(mockTerminalAddress);
|
|
153
|
+
testHook.afterPayRecordedWith{value: 1}(mintWithCredits);
|
|
154
|
+
|
|
155
|
+
assertEq(testHook.balanceOf(beneficiary), 1, "beneficiary still mints the split-bearing NFT");
|
|
156
|
+
assertEq(testHook.payCreditsOf(beneficiary), 1, "stored credits fund essentially the entire mint");
|
|
157
|
+
assertEq(splitReceiver.balance, 1, "split receiver only receives the fresh 1 wei payment");
|
|
158
|
+
assertEq(hookStore.totalCashOutWeight(address(testHook)), 1 ether, "full-price NFT still enters cash-out math");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function test_new_default_reserve_beneficiary_retroactively_dilutes_existing_tiers() public {
|
|
162
|
+
ForTest_JB721TiersHook testHook = _initializeForTestHook(0);
|
|
163
|
+
IJB721TiersHookStore hookStore = testHook.STORE();
|
|
164
|
+
address retroReserveBeneficiary = makeAddr("retroReserveBeneficiary");
|
|
165
|
+
|
|
166
|
+
vm.mockCall(
|
|
167
|
+
mockJBDirectory,
|
|
168
|
+
abi.encodeWithSelector(IJBDirectory.isTerminalOf.selector, projectId, mockTerminalAddress),
|
|
169
|
+
abi.encode(true)
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
JB721TierConfig[] memory initialTier = new JB721TierConfig[](1);
|
|
173
|
+
initialTier[0] = JB721TierConfig({
|
|
174
|
+
price: uint104(1 ether),
|
|
175
|
+
initialSupply: uint32(5),
|
|
176
|
+
votingUnits: 0,
|
|
177
|
+
reserveFrequency: uint16(2),
|
|
178
|
+
reserveBeneficiary: address(0),
|
|
179
|
+
encodedIPFSUri: bytes32(uint256(2)),
|
|
180
|
+
category: uint24(1),
|
|
181
|
+
discountPercent: 0,
|
|
182
|
+
flags: JB721TierConfigFlags({
|
|
183
|
+
allowOwnerMint: false,
|
|
184
|
+
useReserveBeneficiaryAsDefault: false,
|
|
185
|
+
transfersPausable: false,
|
|
186
|
+
useVotingUnits: false,
|
|
187
|
+
cantBeRemoved: false,
|
|
188
|
+
cantIncreaseDiscountPercent: false,
|
|
189
|
+
cantBuyWithCredits: false
|
|
190
|
+
}),
|
|
191
|
+
splitPercent: 0,
|
|
192
|
+
splits: new JBSplit[](0)
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
vm.prank(owner);
|
|
196
|
+
testHook.adjustTiers(initialTier, new uint256[](0));
|
|
197
|
+
|
|
198
|
+
uint16[] memory tierIds = new uint16[](2);
|
|
199
|
+
tierIds[0] = 1;
|
|
200
|
+
tierIds[1] = 1;
|
|
201
|
+
|
|
202
|
+
JBAfterPayRecordedContext memory initialMint = JBAfterPayRecordedContext({
|
|
203
|
+
payer: beneficiary,
|
|
204
|
+
projectId: projectId,
|
|
205
|
+
rulesetId: 0,
|
|
206
|
+
amount: _nativeAmount(2 ether),
|
|
207
|
+
forwardedAmount: _nativeAmount(0),
|
|
208
|
+
weight: 10e18,
|
|
209
|
+
newlyIssuedTokenCount: 0,
|
|
210
|
+
beneficiary: beneficiary,
|
|
211
|
+
hookMetadata: bytes(""),
|
|
212
|
+
payerMetadata: _buildPayMetadata(address(testHook), false, tierIds)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
vm.prank(mockTerminalAddress);
|
|
216
|
+
testHook.afterPayRecordedWith(initialMint);
|
|
217
|
+
|
|
218
|
+
assertEq(
|
|
219
|
+
hookStore.numberOfPendingReservesFor(address(testHook), 1), 0, "no reserve obligation before default set"
|
|
220
|
+
);
|
|
221
|
+
assertEq(testHook.totalCashOutWeight(), 2 ether, "cash-out denominator initially matches sold NFTs");
|
|
222
|
+
|
|
223
|
+
JB721TierConfig[] memory activatingTier = new JB721TierConfig[](1);
|
|
224
|
+
activatingTier[0] = JB721TierConfig({
|
|
225
|
+
price: uint104(2 ether),
|
|
226
|
+
initialSupply: uint32(5),
|
|
227
|
+
votingUnits: 0,
|
|
228
|
+
reserveFrequency: uint16(1),
|
|
229
|
+
reserveBeneficiary: retroReserveBeneficiary,
|
|
230
|
+
encodedIPFSUri: bytes32(uint256(3)),
|
|
231
|
+
category: uint24(2),
|
|
232
|
+
discountPercent: 0,
|
|
233
|
+
flags: JB721TierConfigFlags({
|
|
234
|
+
allowOwnerMint: false,
|
|
235
|
+
useReserveBeneficiaryAsDefault: true,
|
|
236
|
+
transfersPausable: false,
|
|
237
|
+
useVotingUnits: false,
|
|
238
|
+
cantBeRemoved: false,
|
|
239
|
+
cantIncreaseDiscountPercent: false,
|
|
240
|
+
cantBuyWithCredits: false
|
|
241
|
+
}),
|
|
242
|
+
splitPercent: 0,
|
|
243
|
+
splits: new JBSplit[](0)
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
vm.prank(owner);
|
|
247
|
+
testHook.adjustTiers(activatingTier, new uint256[](0));
|
|
248
|
+
|
|
249
|
+
assertEq(
|
|
250
|
+
hookStore.defaultReserveBeneficiaryOf(address(testHook)),
|
|
251
|
+
retroReserveBeneficiary,
|
|
252
|
+
"new tier overwrites the hook-wide default reserve beneficiary"
|
|
253
|
+
);
|
|
254
|
+
assertEq(
|
|
255
|
+
hookStore.numberOfPendingReservesFor(address(testHook), 1),
|
|
256
|
+
1,
|
|
257
|
+
"historic mints on the older tier now retroactively create reserve debt"
|
|
258
|
+
);
|
|
259
|
+
assertEq(
|
|
260
|
+
testHook.totalCashOutWeight(),
|
|
261
|
+
3 ether,
|
|
262
|
+
"cash-out denominator dilutes existing holders before any new payment occurs"
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
testHook.mintPendingReservesFor(1, 1);
|
|
266
|
+
|
|
267
|
+
assertEq(
|
|
268
|
+
testHook.balanceOf(retroReserveBeneficiary), 1, "the new default beneficiary can mint retroactive reserves"
|
|
269
|
+
);
|
|
270
|
+
assertEq(hookStore.numberOfPendingReservesFor(address(testHook), 1), 0, "reserve debt is realized after mint");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import {UnitTestSetup} from "../utils/UnitTestSetup.sol";
|
|
5
|
+
import {JB721TiersHookStore} from "../../src/JB721TiersHookStore.sol";
|
|
6
|
+
import {IJB721TokenUriResolver} from "../../src/interfaces/IJB721TokenUriResolver.sol";
|
|
7
|
+
import {JB721TierConfig} from "../../src/structs/JB721TierConfig.sol";
|
|
8
|
+
|
|
9
|
+
contract CodexNemesisFutureTierPoC is UnitTestSetup {
|
|
10
|
+
function test_futureTierRemovalPersistsIntoNewTierAndBricksMint() external {
|
|
11
|
+
hook = _initHookDefaultTiers(0);
|
|
12
|
+
|
|
13
|
+
uint256[] memory futureTierIds = new uint256[](1);
|
|
14
|
+
futureTierIds[0] = 1;
|
|
15
|
+
|
|
16
|
+
vm.prank(owner);
|
|
17
|
+
hook.adjustTiers(new JB721TierConfig[](0), futureTierIds);
|
|
18
|
+
|
|
19
|
+
(JB721TierConfig[] memory tiersToAdd,) = _createTiers(defaultTierConfig, 1);
|
|
20
|
+
|
|
21
|
+
vm.prank(owner);
|
|
22
|
+
hook.adjustTiers(tiersToAdd, new uint256[](0));
|
|
23
|
+
|
|
24
|
+
assertTrue(hook.STORE().isTierRemoved(address(hook), 1), "future removal flag should persist");
|
|
25
|
+
|
|
26
|
+
uint16[] memory tierIds = new uint16[](1);
|
|
27
|
+
tierIds[0] = 1;
|
|
28
|
+
JB721TiersHookStore store = JB721TiersHookStore(address(hook.STORE()));
|
|
29
|
+
|
|
30
|
+
vm.expectRevert(abi.encodeWithSelector(JB721TiersHookStore.JB721TiersHookStore_TierRemoved.selector, 1));
|
|
31
|
+
vm.prank(address(hook));
|
|
32
|
+
store.recordMint(type(uint256).max, tierIds, false);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function test_futureTierUriCanBePoisonedBeforeTierExists() external {
|
|
36
|
+
hook = _initHookDefaultTiers(0);
|
|
37
|
+
|
|
38
|
+
bytes32 poisonedUri = bytes32(uint256(0x1234));
|
|
39
|
+
|
|
40
|
+
vm.prank(owner);
|
|
41
|
+
hook.setMetadata("", "", "", "", IJB721TokenUriResolver(address(hook)), 1, poisonedUri);
|
|
42
|
+
|
|
43
|
+
(JB721TierConfig[] memory tiersToAdd,) = _createTiers(defaultTierConfig, 1);
|
|
44
|
+
tiersToAdd[0].encodedIPFSUri = bytes32(0);
|
|
45
|
+
|
|
46
|
+
vm.prank(owner);
|
|
47
|
+
hook.adjustTiers(tiersToAdd, new uint256[](0));
|
|
48
|
+
|
|
49
|
+
assertEq(hook.STORE().encodedIPFSUriOf(address(hook), 1), poisonedUri, "future tier inherited stale uri");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity 0.8.28;
|
|
3
|
+
|
|
4
|
+
import "../utils/UnitTestSetup.sol";
|
|
5
|
+
|
|
6
|
+
import {JB721TiersHookProjectDeployer} from "../../src/JB721TiersHookProjectDeployer.sol";
|
|
7
|
+
import {JBDeploy721TiersHookConfig} from "../../src/structs/JBDeploy721TiersHookConfig.sol";
|
|
8
|
+
import {JBLaunchRulesetsConfig} from "../../src/structs/JBLaunchRulesetsConfig.sol";
|
|
9
|
+
import {JBQueueRulesetsConfig} from "../../src/structs/JBQueueRulesetsConfig.sol";
|
|
10
|
+
import {JBPayDataHookRulesetConfig} from "../../src/structs/JBPayDataHookRulesetConfig.sol";
|
|
11
|
+
import {JBPayDataHookRulesetMetadata} from "../../src/structs/JBPayDataHookRulesetMetadata.sol";
|
|
12
|
+
import {JB721TierConfigFlags} from "../../src/structs/JB721TierConfigFlags.sol";
|
|
13
|
+
import {JB721InitTiersConfig} from "../../src/structs/JB721InitTiersConfig.sol";
|
|
14
|
+
import {JB721TiersHookFlags} from "../../src/structs/JB721TiersHookFlags.sol";
|
|
15
|
+
import {IJB721TiersHook} from "../../src/interfaces/IJB721TiersHook.sol";
|
|
16
|
+
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
17
|
+
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
18
|
+
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
19
|
+
import {IJBRulesetApprovalHook} from "@bananapus/core-v6/src/interfaces/IJBRulesetApprovalHook.sol";
|
|
20
|
+
import {IJBTerminal} from "@bananapus/core-v6/src/interfaces/IJBTerminal.sol";
|
|
21
|
+
import {JBPermissionIds} from "@bananapus/permission-ids-v6/src/JBPermissionIds.sol";
|
|
22
|
+
import {JBConstants} from "@bananapus/core-v6/src/libraries/JBConstants.sol";
|
|
23
|
+
import {JBAccountingContext} from "@bananapus/core-v6/src/structs/JBAccountingContext.sol";
|
|
24
|
+
import {JBTerminalConfig} from "@bananapus/core-v6/src/structs/JBTerminalConfig.sol";
|
|
25
|
+
import {JBSplit} from "@bananapus/core-v6/src/structs/JBSplit.sol";
|
|
26
|
+
|
|
27
|
+
contract MockProjectsForNemesis {
|
|
28
|
+
uint256 internal _count;
|
|
29
|
+
address internal _owner;
|
|
30
|
+
|
|
31
|
+
function setup(uint256 count_, address owner_) external {
|
|
32
|
+
_count = count_;
|
|
33
|
+
_owner = owner_;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function count() external view returns (uint256) {
|
|
37
|
+
return _count;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function ownerOf(uint256) external view returns (address) {
|
|
41
|
+
return _owner;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fallback() external {}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
contract StrictControllerForNemesis {
|
|
48
|
+
address internal immutable EXPECTED_CALLER;
|
|
49
|
+
|
|
50
|
+
error UnexpectedCaller(address caller);
|
|
51
|
+
|
|
52
|
+
constructor(address expectedCaller) {
|
|
53
|
+
EXPECTED_CALLER = expectedCaller;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fallback() external payable {
|
|
57
|
+
if (msg.sender != EXPECTED_CALLER) revert UnexpectedCaller(msg.sender);
|
|
58
|
+
bytes memory result = abi.encode(uint256(42));
|
|
59
|
+
assembly {
|
|
60
|
+
return(add(result, 32), mload(result))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
contract Test_CodexNemesisProjectDeployerAuth is UnitTestSetup {
|
|
66
|
+
JB721TiersHookProjectDeployer internal projectDeployer;
|
|
67
|
+
address internal operator = address(0xBEEF);
|
|
68
|
+
uint256 internal testProjectId = 5;
|
|
69
|
+
|
|
70
|
+
function setUp() public override {
|
|
71
|
+
super.setUp();
|
|
72
|
+
|
|
73
|
+
MockProjectsForNemesis projects = new MockProjectsForNemesis();
|
|
74
|
+
vm.etch(mockJBProjects, address(projects).code);
|
|
75
|
+
MockProjectsForNemesis(mockJBProjects).setup(testProjectId, owner);
|
|
76
|
+
|
|
77
|
+
vm.mockCall(mockJBDirectory, abi.encodeWithSelector(IJBDirectory.PROJECTS.selector), abi.encode(mockJBProjects));
|
|
78
|
+
|
|
79
|
+
projectDeployer = new JB721TiersHookProjectDeployer(
|
|
80
|
+
IJBDirectory(mockJBDirectory), IJBPermissions(mockJBPermissions), jbHookDeployer, address(0)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function test_launchRulesetsFor_revertsBecauseControllerSeesProjectDeployerAsCaller() external {
|
|
85
|
+
(JBDeploy721TiersHookConfig memory hookConfig, JBLaunchRulesetsConfig memory launchConfig) =
|
|
86
|
+
_launchConfig(testProjectId);
|
|
87
|
+
|
|
88
|
+
StrictControllerForNemesis controller = new StrictControllerForNemesis(owner);
|
|
89
|
+
|
|
90
|
+
vm.prank(owner);
|
|
91
|
+
vm.expectRevert(
|
|
92
|
+
abi.encodeWithSelector(StrictControllerForNemesis.UnexpectedCaller.selector, address(projectDeployer))
|
|
93
|
+
);
|
|
94
|
+
projectDeployer.launchRulesetsFor(
|
|
95
|
+
testProjectId, hookConfig, launchConfig, IJBController(address(controller)), bytes32(0)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function test_queueRulesetsOf_revertsBecauseControllerSeesProjectDeployerAsCaller() external {
|
|
100
|
+
(JBDeploy721TiersHookConfig memory hookConfig, JBQueueRulesetsConfig memory queueConfig) =
|
|
101
|
+
_queueConfig(testProjectId);
|
|
102
|
+
|
|
103
|
+
StrictControllerForNemesis controller = new StrictControllerForNemesis(owner);
|
|
104
|
+
|
|
105
|
+
vm.prank(owner);
|
|
106
|
+
vm.expectRevert(
|
|
107
|
+
abi.encodeWithSelector(StrictControllerForNemesis.UnexpectedCaller.selector, address(projectDeployer))
|
|
108
|
+
);
|
|
109
|
+
projectDeployer.queueRulesetsOf(
|
|
110
|
+
testProjectId, hookConfig, queueConfig, IJBController(address(controller)), bytes32(0)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function test_launchRulesetsFor_checksLaunchPermission() external {
|
|
115
|
+
(JBDeploy721TiersHookConfig memory hookConfig, JBLaunchRulesetsConfig memory launchConfig) =
|
|
116
|
+
_launchConfig(testProjectId);
|
|
117
|
+
|
|
118
|
+
address account = owner;
|
|
119
|
+
StrictControllerForNemesis controller = new StrictControllerForNemesis(owner);
|
|
120
|
+
|
|
121
|
+
// Grant LAUNCH_RULESETS and SET_TERMINALS, deny QUEUE_RULESETS.
|
|
122
|
+
vm.mockCall(
|
|
123
|
+
mockJBPermissions,
|
|
124
|
+
abi.encodeWithSelector(
|
|
125
|
+
IJBPermissions.hasPermission.selector,
|
|
126
|
+
operator,
|
|
127
|
+
account,
|
|
128
|
+
testProjectId,
|
|
129
|
+
JBPermissionIds.QUEUE_RULESETS,
|
|
130
|
+
true,
|
|
131
|
+
true
|
|
132
|
+
),
|
|
133
|
+
abi.encode(false)
|
|
134
|
+
);
|
|
135
|
+
vm.mockCall(
|
|
136
|
+
mockJBPermissions,
|
|
137
|
+
abi.encodeWithSelector(
|
|
138
|
+
IJBPermissions.hasPermission.selector,
|
|
139
|
+
operator,
|
|
140
|
+
account,
|
|
141
|
+
testProjectId,
|
|
142
|
+
JBPermissionIds.LAUNCH_RULESETS,
|
|
143
|
+
true,
|
|
144
|
+
true
|
|
145
|
+
),
|
|
146
|
+
abi.encode(true)
|
|
147
|
+
);
|
|
148
|
+
vm.mockCall(
|
|
149
|
+
mockJBPermissions,
|
|
150
|
+
abi.encodeWithSelector(
|
|
151
|
+
IJBPermissions.hasPermission.selector,
|
|
152
|
+
operator,
|
|
153
|
+
account,
|
|
154
|
+
testProjectId,
|
|
155
|
+
JBPermissionIds.SET_TERMINALS,
|
|
156
|
+
true,
|
|
157
|
+
true
|
|
158
|
+
),
|
|
159
|
+
abi.encode(true)
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// The permission check passes with LAUNCH_RULESETS. The call proceeds to the controller, which reverts
|
|
163
|
+
// because it sees the deployer contract as the caller rather than the original operator.
|
|
164
|
+
vm.prank(operator);
|
|
165
|
+
vm.expectRevert(
|
|
166
|
+
abi.encodeWithSelector(StrictControllerForNemesis.UnexpectedCaller.selector, address(projectDeployer))
|
|
167
|
+
);
|
|
168
|
+
projectDeployer.launchRulesetsFor(
|
|
169
|
+
testProjectId, hookConfig, launchConfig, IJBController(address(controller)), bytes32(0)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function _launchConfig(uint256 projectId_)
|
|
174
|
+
internal
|
|
175
|
+
view
|
|
176
|
+
returns (JBDeploy721TiersHookConfig memory hookConfig, JBLaunchRulesetsConfig memory launchConfig)
|
|
177
|
+
{
|
|
178
|
+
JB721TierConfig[] memory tierConfigs = new JB721TierConfig[](1);
|
|
179
|
+
tierConfigs[0] = JB721TierConfig({
|
|
180
|
+
price: uint104(10),
|
|
181
|
+
initialSupply: uint32(100),
|
|
182
|
+
votingUnits: uint16(0),
|
|
183
|
+
reserveFrequency: uint16(0),
|
|
184
|
+
reserveBeneficiary: reserveBeneficiary,
|
|
185
|
+
encodedIPFSUri: tokenUris[0],
|
|
186
|
+
category: uint24(1),
|
|
187
|
+
discountPercent: uint8(0),
|
|
188
|
+
flags: JB721TierConfigFlags({
|
|
189
|
+
allowOwnerMint: false,
|
|
190
|
+
useReserveBeneficiaryAsDefault: false,
|
|
191
|
+
transfersPausable: false,
|
|
192
|
+
useVotingUnits: false,
|
|
193
|
+
cantBeRemoved: false,
|
|
194
|
+
cantIncreaseDiscountPercent: false,
|
|
195
|
+
cantBuyWithCredits: false
|
|
196
|
+
}),
|
|
197
|
+
splitPercent: 0,
|
|
198
|
+
splits: new JBSplit[](0)
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
hookConfig = JBDeploy721TiersHookConfig({
|
|
202
|
+
name: name,
|
|
203
|
+
symbol: symbol,
|
|
204
|
+
baseUri: baseUri,
|
|
205
|
+
tokenUriResolver: IJB721TokenUriResolver(address(0)),
|
|
206
|
+
contractUri: contractUri,
|
|
207
|
+
tiersConfig: JB721InitTiersConfig({
|
|
208
|
+
tiers: tierConfigs, currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18
|
|
209
|
+
}),
|
|
210
|
+
flags: JB721TiersHookFlags({
|
|
211
|
+
preventOverspending: false,
|
|
212
|
+
issueTokensForSplits: false,
|
|
213
|
+
noNewTiersWithReserves: true,
|
|
214
|
+
noNewTiersWithVotes: false,
|
|
215
|
+
noNewTiersWithOwnerMinting: true
|
|
216
|
+
})
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
JBPayDataHookRulesetConfig[] memory rulesetConfigs = new JBPayDataHookRulesetConfig[](1);
|
|
220
|
+
rulesetConfigs[0].mustStartAtOrAfter = 0;
|
|
221
|
+
rulesetConfigs[0].duration = 14;
|
|
222
|
+
rulesetConfigs[0].weight = 1e18;
|
|
223
|
+
rulesetConfigs[0].weightCutPercent = 0;
|
|
224
|
+
rulesetConfigs[0].approvalHook = IJBRulesetApprovalHook(address(0));
|
|
225
|
+
rulesetConfigs[0].metadata = JBPayDataHookRulesetMetadata({
|
|
226
|
+
reservedPercent: 5000,
|
|
227
|
+
cashOutTaxRate: 5000,
|
|
228
|
+
baseCurrency: uint32(uint160(JBConstants.NATIVE_TOKEN)),
|
|
229
|
+
pausePay: false,
|
|
230
|
+
pauseCreditTransfers: false,
|
|
231
|
+
allowOwnerMinting: false,
|
|
232
|
+
allowSetCustomToken: false,
|
|
233
|
+
allowTerminalMigration: false,
|
|
234
|
+
allowSetTerminals: false,
|
|
235
|
+
allowSetController: false,
|
|
236
|
+
ownerMustSendPayouts: false,
|
|
237
|
+
allowAddAccountingContext: false,
|
|
238
|
+
allowAddPriceFeed: false,
|
|
239
|
+
holdFees: false,
|
|
240
|
+
useTotalSurplusForCashOuts: false,
|
|
241
|
+
useDataHookForCashOut: false,
|
|
242
|
+
metadata: 0x00
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
JBAccountingContext[] memory accountingContexts = new JBAccountingContext[](1);
|
|
246
|
+
accountingContexts[0] = JBAccountingContext({
|
|
247
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN)), decimals: 18, token: JBConstants.NATIVE_TOKEN
|
|
248
|
+
});
|
|
249
|
+
JBTerminalConfig[] memory terminalConfigs = new JBTerminalConfig[](1);
|
|
250
|
+
terminalConfigs[0] = JBTerminalConfig({
|
|
251
|
+
terminal: IJBTerminal(mockTerminalAddress), accountingContextsToAccept: accountingContexts
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
launchConfig = JBLaunchRulesetsConfig({
|
|
255
|
+
projectId: uint56(projectId_),
|
|
256
|
+
rulesetConfigurations: rulesetConfigs,
|
|
257
|
+
terminalConfigurations: terminalConfigs,
|
|
258
|
+
memo: "launch"
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function _queueConfig(uint256 projectId_)
|
|
263
|
+
internal
|
|
264
|
+
view
|
|
265
|
+
returns (JBDeploy721TiersHookConfig memory hookConfig, JBQueueRulesetsConfig memory queueConfig)
|
|
266
|
+
{
|
|
267
|
+
JBLaunchRulesetsConfig memory launchConfig;
|
|
268
|
+
(hookConfig, launchConfig) = _launchConfig(projectId_);
|
|
269
|
+
queueConfig = JBQueueRulesetsConfig({
|
|
270
|
+
projectId: uint56(projectId_), rulesetConfigurations: launchConfig.rulesetConfigurations, memo: "queue"
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|