@bananapus/omnichain-deployers-v6 0.0.23 → 0.0.24
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/ARCHITECTURE.md +10 -0
- package/CHANGELOG.md +5 -0
- package/RISKS.md +1 -1
- package/USER_JOURNEYS.md +5 -4
- package/package.json +5 -5
- package/src/JBOmnichainDeployer.sol +19 -4
- package/test/JBOmnichainDeployer.t.sol +27 -4
- package/test/OmnichainDeployerEdgeCases.t.sol +12 -0
- package/test/TestAuditGaps.sol +21 -10
- package/test/audit/CarryForwardRejectedHook.t.sol +12 -2
- package/test/audit/JBOmnichainDeployer.t.sol +14 -1
package/ARCHITECTURE.md
CHANGED
|
@@ -31,6 +31,15 @@ caller
|
|
|
31
31
|
-> project ownership is transferred to the intended owner
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### 721 Hook Carry-Forward (Queue Path)
|
|
35
|
+
|
|
36
|
+
When `queueRulesetsOf` is called without new tiers, the deployer carries the existing 721 hook forward. The source ruleset is chosen with this precedence:
|
|
37
|
+
|
|
38
|
+
1. **Latest queued ruleset** — if its approval status is `Approved` or `Empty` (no approval hook) and it has a hook config stored in the deployer.
|
|
39
|
+
2. **Current active ruleset** — fallback when no qualifying queued ruleset exists.
|
|
40
|
+
|
|
41
|
+
This ensures that a recently queued (and approved) ruleset's hook config takes precedence over a potentially stale active ruleset. The `useDataHookForCashOut` flag is also preserved from whichever source ruleset is selected.
|
|
42
|
+
|
|
34
43
|
### Pay And Cash-Out Wrapping
|
|
35
44
|
|
|
36
45
|
```text
|
|
@@ -47,6 +56,7 @@ runtime callback
|
|
|
47
56
|
- Hook order matters: the 721 hook runs first, and the extra hook receives the updated context.
|
|
48
57
|
- The deployer's predicted ruleset IDs must stay aligned with `JBRulesets` behavior; the storage keys depend on it.
|
|
49
58
|
- Every project launched through this repo gets a 721 hook surface, even if it starts with zero tiers.
|
|
59
|
+
- Carry-forward must prefer the latest approved queued ruleset over the current ruleset to avoid losing hook config from a recently queued update.
|
|
50
60
|
|
|
51
61
|
## Where Complexity Lives
|
|
52
62
|
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v6 post-audit (M-11 fix)
|
|
4
|
+
|
|
5
|
+
- **Carry-forward hook selection improved.** `queueRulesetsOf` now checks `latestQueuedOf(projectId)` before falling back to `currentOf(projectId)` when carrying forward a 721 hook. Previously it only read `currentOf`, which could miss a recently queued (and approved) ruleset's hook config. The source ruleset must have approval status `Approved` or `Empty` and a stored hook config in the deployer.
|
|
6
|
+
- The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected during carry-forward.
|
|
7
|
+
|
|
3
8
|
## Scope
|
|
4
9
|
|
|
5
10
|
This file describes the verified change from `nana-omnichain-deployers-v5` to the current `nana-omnichain-deployers-v6` repo.
|
package/RISKS.md
CHANGED
|
@@ -52,7 +52,7 @@ This file focuses on the risks in the deployer layer that launches Juicebox proj
|
|
|
52
52
|
## 6. Integration Risks
|
|
53
53
|
|
|
54
54
|
- **Hook config keyed by predicted rulesetId.** Configs stored at `block.timestamp + i` must match the actual rulesetId assigned by the controller. If the controller assigns different IDs (e.g., due to approval hook delays), the stored configs become unreachable -- payments/cashouts fall through to default behavior (no 721 handling, no extra hook).
|
|
55
|
-
- **Carried-forward 721 hook on queue.** When `tiers.length == 0`, `queueRulesetsOf` carries forward the hook from `
|
|
55
|
+
- **Carried-forward 721 hook on queue.** When `tiers.length == 0`, `queueRulesetsOf` carries forward the hook from a previous ruleset. The source is selected by first checking `latestQueuedOf(projectId)` — if the queued ruleset's approval status is `Approved` or `Empty` and it has a stored hook config, that config is used. Otherwise it falls back to `currentOf(projectId)`. If neither has a hook deployed through this deployer, the mapping is empty and the call reverts with `JBOmnichainDeployer_InvalidHook`. The `useDataHookForCashOut` flag is also preserved from whichever source ruleset is selected.
|
|
56
56
|
- **ERC721Receiver restriction.** `onERC721Received` only accepts from `PROJECTS`. Any other NFTs sent to this contract are permanently lost.
|
|
57
57
|
- **Cross-reference: sucker registration.** The deployer grants `MAP_SUCKER_TOKEN` to `SUCKER_REGISTRY` with `projectId=0` (wildcard). This means the registry can map tokens for ALL projects deployed through this deployer. See [nana-suckers-v6 RISKS.md](../nana-suckers-v6/RISKS.md) for the full sucker lifecycle risks.
|
|
58
58
|
- **Cross-reference: core reentrancy.** The deployer delegates to `JBController` and `JBMultiTerminal` for all fund operations. See [nana-core-v6 RISKS.md](../nana-core-v6/RISKS.md) section 3 for the reentrancy surface of these contracts.
|
package/USER_JOURNEYS.md
CHANGED
|
@@ -36,10 +36,11 @@
|
|
|
36
36
|
**Success:** the deployer carries the existing hook forward, stores it against the queued ruleset, and preserves the ownership and wrapper assumptions bridge flows need.
|
|
37
37
|
|
|
38
38
|
**Flow**
|
|
39
|
-
1. Queue the next ruleset through the deployer using the path that reuses the existing 721 hook.
|
|
40
|
-
2.
|
|
41
|
-
3.
|
|
42
|
-
4.
|
|
39
|
+
1. Queue the next ruleset through the deployer using the path that reuses the existing 721 hook (pass zero tiers).
|
|
40
|
+
2. The deployer selects the source hook: it first checks the latest queued ruleset (if approved or with no approval hook), then falls back to the current active ruleset. This prevents losing a recently queued hook config.
|
|
41
|
+
3. The `useDataHookForCashOut` flag is preserved from whichever source ruleset is selected.
|
|
42
|
+
4. Validate the controller and queued ruleset inputs before relying on the result.
|
|
43
|
+
5. Confirm the queued ruleset now points at the carried-forward hook rather than accidentally dropping the 721 layer.
|
|
43
44
|
|
|
44
45
|
## Journey 4: Compose A Tiered 721 Hook With A Custom Extra Hook
|
|
45
46
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananapus/omnichain-deployers-v6",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
"artifacts": "source ./.env && npx sphinx artifacts --org-id 'ea165b21-7cdc-4d7b-be59-ecdd4c26bee4' --project-name 'nana-omnichain-deployers-v6'"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@bananapus/721-hook-v6": "^0.0.
|
|
21
|
-
"@bananapus/buyback-hook-v6": "^0.0.
|
|
22
|
-
"@bananapus/core-v6": "^0.0.
|
|
20
|
+
"@bananapus/721-hook-v6": "^0.0.32",
|
|
21
|
+
"@bananapus/buyback-hook-v6": "^0.0.26",
|
|
22
|
+
"@bananapus/core-v6": "^0.0.32",
|
|
23
23
|
"@bananapus/ownable-v6": "^0.0.17",
|
|
24
24
|
"@bananapus/permission-ids-v6": "^0.0.15",
|
|
25
|
-
"@bananapus/suckers-v6": "^0.0.
|
|
25
|
+
"@bananapus/suckers-v6": "^0.0.22",
|
|
26
26
|
"@openzeppelin/contracts": "^5.6.1",
|
|
27
27
|
"@uniswap/v4-core": "^1.0.2"
|
|
28
28
|
},
|
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
import {IJB721TiersHook} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHook.sol";
|
|
5
5
|
import {IJB721TiersHookDeployer} from "@bananapus/721-hook-v6/src/interfaces/IJB721TiersHookProjectDeployer.sol";
|
|
6
|
+
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
6
7
|
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
7
8
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
8
9
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
@@ -808,10 +809,24 @@ contract JBOmnichainDeployer is
|
|
|
808
809
|
// Use the caller-provided flag when deploying a new hook.
|
|
809
810
|
use721ForCashOut = deploy721Config.useDataHookForCashOut;
|
|
810
811
|
} else {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
812
|
+
uint256 sourceRulesetId;
|
|
813
|
+
{
|
|
814
|
+
// First try the latest queued ruleset — if it's been explicitly approved
|
|
815
|
+
// (or has no approval hook), its hook config should take precedence.
|
|
816
|
+
(JBRuleset memory latestQueued, JBApprovalStatus approvalStatus) =
|
|
817
|
+
controller.RULESETS().latestQueuedOf(projectId);
|
|
818
|
+
if (
|
|
819
|
+
latestQueued.id != 0
|
|
820
|
+
&& (approvalStatus == JBApprovalStatus.Approved || approvalStatus == JBApprovalStatus.Empty)
|
|
821
|
+
&& address(_tiered721HookOf[projectId][latestQueued.id].hook) != address(0)
|
|
822
|
+
) {
|
|
823
|
+
sourceRulesetId = latestQueued.id;
|
|
824
|
+
} else {
|
|
825
|
+
// Fall back to the current (active, approved) ruleset.
|
|
826
|
+
sourceRulesetId = controller.RULESETS().currentOf(projectId).id;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
JBTiered721HookConfig memory previousConfig = _tiered721HookOf[projectId][sourceRulesetId];
|
|
815
830
|
hook = previousConfig.hook;
|
|
816
831
|
// Revert if no hook exists to carry forward — this means no tiers were provided and
|
|
817
832
|
// no previous ruleset had a 721 hook deployed through this contract.
|
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
|
+
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
6
7
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
7
8
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
9
|
import {IJBPermissions} from "@bananapus/core-v6/src/interfaces/IJBPermissions.sol";
|
|
@@ -393,10 +394,22 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
393
394
|
abi.encode(launchTimestamp)
|
|
394
395
|
);
|
|
395
396
|
|
|
396
|
-
// Mock
|
|
397
|
-
|
|
397
|
+
// Mock latestQueuedOf to return the launch ruleset with Empty approval status.
|
|
398
|
+
{
|
|
399
|
+
JBRuleset memory latestQueuedRuleset;
|
|
400
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
401
|
+
latestQueuedRuleset.id = uint48(launchTimestamp);
|
|
402
|
+
vm.mockCall(
|
|
403
|
+
address(rulesets),
|
|
404
|
+
abi.encodeWithSelector(IJBRulesets.latestQueuedOf.selector, projectId),
|
|
405
|
+
abi.encode(latestQueuedRuleset, JBApprovalStatus.Empty)
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Mock currentOf as a fallback (not reached when latestQueuedOf succeeds).
|
|
398
410
|
{
|
|
399
411
|
JBRuleset memory currentRuleset;
|
|
412
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
400
413
|
currentRuleset.id = uint48(launchTimestamp);
|
|
401
414
|
vm.mockCall(
|
|
402
415
|
address(rulesets),
|
|
@@ -485,9 +498,19 @@ contract TestJBOmnichainDeployer is Test {
|
|
|
485
498
|
abi.encode(launchTimestamp)
|
|
486
499
|
);
|
|
487
500
|
|
|
488
|
-
// Mock
|
|
489
|
-
|
|
501
|
+
// Mock latestQueuedOf to return the launch ruleset with Empty approval status.
|
|
502
|
+
JBRuleset memory latestQueuedRuleset;
|
|
503
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
504
|
+
latestQueuedRuleset.id = uint48(launchTimestamp);
|
|
505
|
+
vm.mockCall(
|
|
506
|
+
address(rulesets),
|
|
507
|
+
abi.encodeWithSelector(IJBRulesets.latestQueuedOf.selector, projectId),
|
|
508
|
+
abi.encode(latestQueuedRuleset, JBApprovalStatus.Empty)
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
// Mock currentOf as a fallback (not reached when latestQueuedOf succeeds).
|
|
490
512
|
JBRuleset memory currentRuleset;
|
|
513
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
491
514
|
currentRuleset.id = uint48(launchTimestamp);
|
|
492
515
|
vm.mockCall(
|
|
493
516
|
address(rulesets),
|
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
|
+
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
6
7
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
7
8
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
9
|
import {IJBCashOutHook} from "@bananapus/core-v6/src/interfaces/IJBCashOutHook.sol";
|
|
@@ -614,6 +615,17 @@ contract OmnichainDeployerEdgeCases is Test {
|
|
|
614
615
|
abi.encode(uint256(50)) // A past ruleset ID — no hook stored at this ID
|
|
615
616
|
);
|
|
616
617
|
|
|
618
|
+
// Mock latestQueuedOf to return a ruleset with id=50 (no hook stored for this id via the deployer).
|
|
619
|
+
// The carry-forward logic checks latestQueuedOf first; since no hook was stored at id=50,
|
|
620
|
+
// it falls through to currentOf.
|
|
621
|
+
JBRuleset memory latestQueuedRuleset;
|
|
622
|
+
latestQueuedRuleset.id = uint48(50);
|
|
623
|
+
vm.mockCall(
|
|
624
|
+
address(rulesets),
|
|
625
|
+
abi.encodeWithSelector(IJBRulesets.latestQueuedOf.selector, projectId),
|
|
626
|
+
abi.encode(latestQueuedRuleset, JBApprovalStatus.Empty)
|
|
627
|
+
);
|
|
628
|
+
|
|
617
629
|
// Mock currentOf to return a ruleset with id=50 (no hook stored for this id via the deployer).
|
|
618
630
|
JBRuleset memory currentRuleset;
|
|
619
631
|
currentRuleset.id = uint48(50);
|
package/test/TestAuditGaps.sol
CHANGED
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
|
+
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
6
7
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
7
8
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
8
9
|
import {IJBPayHook} from "@bananapus/core-v6/src/interfaces/IJBPayHook.sol";
|
|
@@ -539,6 +540,7 @@ contract TestAuditGaps is Test {
|
|
|
539
540
|
// latestRulesetIdOf still = BASE_TIME (from launch).
|
|
540
541
|
// Guard: BASE_TIME >= BASE_TIME + 1 -> false -> passes.
|
|
541
542
|
_mockLatestRulesetId(BASE_TIME);
|
|
543
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
542
544
|
_mockCurrentRulesetId(BASE_TIME);
|
|
543
545
|
|
|
544
546
|
uint256 expectedQueuedId = BASE_TIME + 1;
|
|
@@ -566,6 +568,7 @@ contract TestAuditGaps is Test {
|
|
|
566
568
|
// Warp forward 1 second for first queue.
|
|
567
569
|
vm.warp(BASE_TIME + 1);
|
|
568
570
|
_mockLatestRulesetId(BASE_TIME);
|
|
571
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
569
572
|
_mockCurrentRulesetId(BASE_TIME);
|
|
570
573
|
|
|
571
574
|
uint256 firstQueueTime = BASE_TIME + 1;
|
|
@@ -596,6 +599,7 @@ contract TestAuditGaps is Test {
|
|
|
596
599
|
|
|
597
600
|
// Warp forward 1 more second. Now block.timestamp = BASE_TIME + 2.
|
|
598
601
|
vm.warp(BASE_TIME + 2);
|
|
602
|
+
_mockLatestQueuedRuleset(firstQueueTime, JBApprovalStatus.Empty);
|
|
599
603
|
|
|
600
604
|
uint256 secondQueueTime = BASE_TIME + 2;
|
|
601
605
|
vm.mockCall(
|
|
@@ -674,6 +678,7 @@ contract TestAuditGaps is Test {
|
|
|
674
678
|
// Warp past the latestRulesetId: block.timestamp = BASE_TIME + 3.
|
|
675
679
|
vm.warp(BASE_TIME + 3);
|
|
676
680
|
_mockLatestRulesetId(latestRulesetId);
|
|
681
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
677
682
|
_mockCurrentRulesetId(BASE_TIME);
|
|
678
683
|
|
|
679
684
|
uint256 expectedQueuedId = BASE_TIME + 3;
|
|
@@ -702,6 +707,7 @@ contract TestAuditGaps is Test {
|
|
|
702
707
|
// Warp forward.
|
|
703
708
|
vm.warp(BASE_TIME + 100);
|
|
704
709
|
_mockLatestRulesetId(BASE_TIME);
|
|
710
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
705
711
|
_mockCurrentRulesetId(BASE_TIME);
|
|
706
712
|
|
|
707
713
|
uint256 expectedQueuedId = BASE_TIME + 100;
|
|
@@ -742,16 +748,8 @@ contract TestAuditGaps is Test {
|
|
|
742
748
|
// Warp forward.
|
|
743
749
|
vm.warp(BASE_TIME + 50);
|
|
744
750
|
_mockLatestRulesetId(BASE_TIME);
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
// stored 721 hook.
|
|
748
|
-
JBRuleset memory currentRuleset;
|
|
749
|
-
currentRuleset.id = uint48(BASE_TIME);
|
|
750
|
-
vm.mockCall(
|
|
751
|
-
address(rulesetsContract),
|
|
752
|
-
abi.encodeWithSelector(IJBRulesets.currentOf.selector, projectId),
|
|
753
|
-
abi.encode(currentRuleset)
|
|
754
|
-
);
|
|
751
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
752
|
+
_mockCurrentRulesetId(BASE_TIME);
|
|
755
753
|
|
|
756
754
|
uint256 expectedQueuedId = BASE_TIME + 50;
|
|
757
755
|
vm.mockCall(
|
|
@@ -834,6 +832,7 @@ contract TestAuditGaps is Test {
|
|
|
834
832
|
|
|
835
833
|
vm.warp(BASE_TIME + 10);
|
|
836
834
|
_mockLatestRulesetId(BASE_TIME);
|
|
835
|
+
_mockLatestQueuedRuleset(BASE_TIME, JBApprovalStatus.Empty);
|
|
837
836
|
_mockCurrentRulesetId(BASE_TIME);
|
|
838
837
|
|
|
839
838
|
uint256 expectedQueuedId = BASE_TIME + 10;
|
|
@@ -883,12 +882,24 @@ contract TestAuditGaps is Test {
|
|
|
883
882
|
|
|
884
883
|
function _mockCurrentRulesetId(uint256 currentRulesetId) internal {
|
|
885
884
|
JBRuleset memory r;
|
|
885
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
886
886
|
r.id = uint48(currentRulesetId);
|
|
887
887
|
vm.mockCall(
|
|
888
888
|
address(rulesetsContract), abi.encodeWithSelector(IJBRulesets.currentOf.selector, projectId), abi.encode(r)
|
|
889
889
|
);
|
|
890
890
|
}
|
|
891
891
|
|
|
892
|
+
function _mockLatestQueuedRuleset(uint256 rulesetId, JBApprovalStatus status) internal {
|
|
893
|
+
JBRuleset memory r;
|
|
894
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
895
|
+
r.id = uint48(rulesetId);
|
|
896
|
+
vm.mockCall(
|
|
897
|
+
address(rulesetsContract),
|
|
898
|
+
abi.encodeWithSelector(IJBRulesets.latestQueuedOf.selector, projectId),
|
|
899
|
+
abi.encode(r, status)
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
892
903
|
function _makePayContext(uint256 pid, uint256 rid) internal returns (JBBeforePayRecordedContext memory) {
|
|
893
904
|
return JBBeforePayRecordedContext({
|
|
894
905
|
terminal: makeAddr("terminal"),
|
|
@@ -30,6 +30,7 @@ import {JBOmnichain721Config} from "../../src/structs/JBOmnichain721Config.sol";
|
|
|
30
30
|
import {JBSuckerDeploymentConfig} from "../../src/structs/JBSuckerDeploymentConfig.sol";
|
|
31
31
|
|
|
32
32
|
contract RejectingApprovalHook is ERC165, IJBRulesetApprovalHook {
|
|
33
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
33
34
|
function DURATION() external pure override returns (uint256) {
|
|
34
35
|
return 0;
|
|
35
36
|
}
|
|
@@ -73,10 +74,12 @@ contract SequentialHookDeployer is IJB721TiersHookDeployer {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
contract MockSuckerRegistryCarryForward is IJBSuckerRegistry {
|
|
77
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
76
78
|
function DIRECTORY() external pure override returns (IJBDirectory) {
|
|
77
79
|
return IJBDirectory(address(0));
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
80
83
|
function PROJECTS() external pure override returns (IJBProjects) {
|
|
81
84
|
return IJBProjects(address(0));
|
|
82
85
|
}
|
|
@@ -97,6 +100,7 @@ contract MockSuckerRegistryCarryForward is IJBSuckerRegistry {
|
|
|
97
100
|
return new address[](0);
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
// forge-lint: disable-next-line(mixed-case-function)
|
|
100
104
|
function MAX_TO_REMOTE_FEE() external pure override returns (uint256) {
|
|
101
105
|
return 0;
|
|
102
106
|
}
|
|
@@ -236,7 +240,8 @@ contract CarryForwardRejectedHookTest is TestBaseWorkflow {
|
|
|
236
240
|
|
|
237
241
|
function _default721Config() internal pure returns (JBOmnichain721Config memory config) {
|
|
238
242
|
config.deployTiersHookConfig.tiersConfig.currency =
|
|
239
|
-
|
|
243
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
244
|
+
uint32(uint160(address(0x000000000000000000000000000000000000EEEe)));
|
|
240
245
|
config.deployTiersHookConfig.tiersConfig.decimals = 18;
|
|
241
246
|
}
|
|
242
247
|
|
|
@@ -255,6 +260,7 @@ contract CarryForwardRejectedHookTest is TestBaseWorkflow {
|
|
|
255
260
|
metadata: JBRulesetMetadata({
|
|
256
261
|
reservedPercent: 0,
|
|
257
262
|
cashOutTaxRate: 0,
|
|
263
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
258
264
|
baseCurrency: uint32(uint160(address(0x000000000000000000000000000000000000EEEe))),
|
|
259
265
|
pausePay: false,
|
|
260
266
|
pauseCreditTransfers: false,
|
|
@@ -290,6 +296,7 @@ contract CarryForwardRejectedHookTest is TestBaseWorkflow {
|
|
|
290
296
|
jbPermissions()
|
|
291
297
|
.setPermissionsFor(
|
|
292
298
|
owner,
|
|
299
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
293
300
|
JBPermissionsData({operator: address(this), projectId: uint64(projectId), permissionIds: permissionIds})
|
|
294
301
|
);
|
|
295
302
|
|
|
@@ -298,7 +305,10 @@ contract CarryForwardRejectedHookTest is TestBaseWorkflow {
|
|
|
298
305
|
.setPermissionsFor(
|
|
299
306
|
owner,
|
|
300
307
|
JBPermissionsData({
|
|
301
|
-
operator: address(deployer),
|
|
308
|
+
operator: address(deployer),
|
|
309
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
310
|
+
projectId: uint64(projectId),
|
|
311
|
+
permissionIds: permissionIds
|
|
302
312
|
})
|
|
303
313
|
);
|
|
304
314
|
}
|
|
@@ -3,6 +3,7 @@ pragma solidity 0.8.28;
|
|
|
3
3
|
|
|
4
4
|
import {Test} from "forge-std/Test.sol";
|
|
5
5
|
|
|
6
|
+
import {JBApprovalStatus} from "@bananapus/core-v6/src/enums/JBApprovalStatus.sol";
|
|
6
7
|
import {JBPermissioned} from "@bananapus/core-v6/src/abstract/JBPermissioned.sol";
|
|
7
8
|
import {IJBController} from "@bananapus/core-v6/src/interfaces/IJBController.sol";
|
|
8
9
|
import {IJBDirectory} from "@bananapus/core-v6/src/interfaces/IJBDirectory.sol";
|
|
@@ -197,8 +198,20 @@ contract JBOmnichainDeployerTest is Test {
|
|
|
197
198
|
abi.encode(initialRulesetId)
|
|
198
199
|
);
|
|
199
200
|
|
|
200
|
-
// Mock
|
|
201
|
+
// Mock latestQueuedOf to return the initial ruleset with Empty approval status,
|
|
202
|
+
// so the carry-forward logic finds the 721 hook from the latest queued ruleset.
|
|
203
|
+
JBRuleset memory latestQueuedRuleset;
|
|
204
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
205
|
+
latestQueuedRuleset.id = uint48(initialRulesetId);
|
|
206
|
+
vm.mockCall(
|
|
207
|
+
address(rulesets),
|
|
208
|
+
abi.encodeWithSelector(IJBRulesets.latestQueuedOf.selector, PROJECT_ID),
|
|
209
|
+
abi.encode(latestQueuedRuleset, JBApprovalStatus.Empty)
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Mock currentOf as a fallback (not reached in this scenario since latestQueuedOf succeeds).
|
|
201
213
|
JBRuleset memory currentRuleset;
|
|
214
|
+
// forge-lint: disable-next-line(unsafe-typecast)
|
|
202
215
|
currentRuleset.id = uint48(initialRulesetId);
|
|
203
216
|
vm.mockCall(
|
|
204
217
|
address(rulesets),
|