@bananapus/omnichain-deployers-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/README.md +1 -1
- package/package.json +3 -3
- package/references/operations.md +1 -1
- package/references/runtime.md +1 -1
- package/src/JBOmnichainDeployer.sol +53 -42
package/README.md
CHANGED
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.39",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@bananapus/721-hook-v6": "0.0.43",
|
|
30
|
-
"@bananapus/core-v6": "0.0.
|
|
30
|
+
"@bananapus/core-v6": "0.0.44",
|
|
31
31
|
"@bananapus/ownable-v6": "0.0.24",
|
|
32
32
|
"@bananapus/permission-ids-v6": "0.0.22",
|
|
33
|
-
"@bananapus/suckers-v6": "0.0.
|
|
33
|
+
"@bananapus/suckers-v6": "0.0.37",
|
|
34
34
|
"@openzeppelin/contracts": "5.6.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
package/references/operations.md
CHANGED
|
@@ -24,5 +24,5 @@
|
|
|
24
24
|
## Useful Proof Points
|
|
25
25
|
|
|
26
26
|
- [`test/JBOmnichainDeployer.t.sol`](../test/JBOmnichainDeployer.t.sol) for baseline deploy and queue flows.
|
|
27
|
-
- [`test/
|
|
27
|
+
- [`test/TestRegressionGaps.sol`](../test/TestRegressionGaps.sol) for pinned edge cases.
|
|
28
28
|
- [`test/Tiered721HookComposition.t.sol`](../test/Tiered721HookComposition.t.sol) when cross-repo integration behavior matters more than isolated unit logic.
|
package/references/runtime.md
CHANGED
|
@@ -25,4 +25,4 @@
|
|
|
25
25
|
- [`test/Tiered721HookComposition.t.sol`](../test/Tiered721HookComposition.t.sol) for hook-composition behavior.
|
|
26
26
|
- [`test/JBOmnichainDeployerGuard.t.sol`](../test/JBOmnichainDeployerGuard.t.sol) and [`test/OmnichainDeployerAttacks.t.sol`](../test/OmnichainDeployerAttacks.t.sol) for safety properties.
|
|
27
27
|
- [`test/OmnichainDeployerReentrancy.t.sol`](../test/OmnichainDeployerReentrancy.t.sol) for wrapper security assumptions.
|
|
28
|
-
- [`test/OmnichainDeployerEdgeCases.t.sol`](../test/OmnichainDeployerEdgeCases.t.sol), [`test/JBOmnichainDeployer.t.sol`](../test/JBOmnichainDeployer.t.sol), and [`test/
|
|
28
|
+
- [`test/OmnichainDeployerEdgeCases.t.sol`](../test/OmnichainDeployerEdgeCases.t.sol), [`test/JBOmnichainDeployer.t.sol`](../test/JBOmnichainDeployer.t.sol), and [`test/TestRegressionGaps.sol`](../test/TestRegressionGaps.sol) for edge behavior.
|
|
@@ -55,24 +55,26 @@ contract JBOmnichainDeployer is
|
|
|
55
55
|
//*********************************************************************//
|
|
56
56
|
|
|
57
57
|
/// @notice Thrown when the provided controller does not match the project's controller in the directory.
|
|
58
|
-
error JBOmnichainDeployer_ControllerMismatch(
|
|
58
|
+
error JBOmnichainDeployer_ControllerMismatch(
|
|
59
|
+
uint256 projectId, address expectedController, address actualController
|
|
60
|
+
);
|
|
59
61
|
|
|
60
|
-
/// @notice Thrown when a data hook is
|
|
61
|
-
error JBOmnichainDeployer_InvalidHook();
|
|
62
|
+
/// @notice Thrown when a data hook is invalid for the project ruleset being configured.
|
|
63
|
+
error JBOmnichainDeployer_InvalidHook(address hook, uint256 projectId, uint256 rulesetId);
|
|
62
64
|
|
|
63
65
|
/// @notice Thrown when an empty `rulesetConfigurations` array is passed to a simplified overload that needs at
|
|
64
66
|
/// least one ruleset to derive a default 721 config.
|
|
65
|
-
error JBOmnichainDeployer_NoRulesetConfigurations();
|
|
66
|
-
|
|
67
|
-
/// @notice Thrown when the project ID returned by the controller does not match the expected project ID.
|
|
68
|
-
error JBOmnichainDeployer_ProjectIdMismatch();
|
|
67
|
+
error JBOmnichainDeployer_NoRulesetConfigurations(uint256 rulesetConfigurationCount);
|
|
69
68
|
|
|
70
69
|
/// @notice Thrown when queueing rulesets for a project whose latest ruleset was already queued in the same block.
|
|
71
70
|
/// @dev Ruleset IDs are predicted as `block.timestamp + i`. This prediction fails if
|
|
72
71
|
/// `latestRulesetIdOf >= block.timestamp`, which can only happen if rulesets were already queued in the same block.
|
|
73
|
-
error JBOmnichainDeployer_RulesetIdsUnpredictable(
|
|
72
|
+
error JBOmnichainDeployer_RulesetIdsUnpredictable(
|
|
73
|
+
uint256 projectId, uint256 latestRulesetId, uint256 currentTimestamp
|
|
74
|
+
);
|
|
74
75
|
|
|
75
|
-
|
|
76
|
+
/// @notice Thrown when this contract receives a project NFT from an unexpected sender or transfer.
|
|
77
|
+
error JBOmnichainDeployer_UnexpectedNFTReceived(address caller, address from, uint256 tokenId);
|
|
76
78
|
|
|
77
79
|
//*********************************************************************//
|
|
78
80
|
// --------------- public immutable stored properties ---------------- //
|
|
@@ -172,7 +174,6 @@ contract JBOmnichainDeployer is
|
|
|
172
174
|
// Deploy the suckers.
|
|
173
175
|
// Note: the salt includes `_msgSender()` for replay protection. Cross-chain deterministic
|
|
174
176
|
// address matching requires using the same sender address on each chain.
|
|
175
|
-
// slither-disable-next-line unused-return
|
|
176
177
|
suckers = SUCKER_REGISTRY.deploySuckersFor({
|
|
177
178
|
projectId: projectId,
|
|
178
179
|
salt: keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender())),
|
|
@@ -325,10 +326,12 @@ contract JBOmnichainDeployer is
|
|
|
325
326
|
});
|
|
326
327
|
}
|
|
327
328
|
|
|
328
|
-
/// @dev Make sure this contract can only receive project NFTs from `JBProjects
|
|
329
|
-
function onERC721Received(address, address, uint256, bytes calldata) external view returns (bytes4) {
|
|
330
|
-
//
|
|
331
|
-
if (msg.sender != address(PROJECTS)
|
|
329
|
+
/// @dev Make sure this contract can only receive project NFTs minted from `JBProjects` (not transferred).
|
|
330
|
+
function onERC721Received(address, address from, uint256 tokenId, bytes calldata) external view returns (bytes4) {
|
|
331
|
+
// Only accept mints (from == address(0)) from the `JBProjects` contract, not arbitrary transfers.
|
|
332
|
+
if (msg.sender != address(PROJECTS) || from != address(0)) {
|
|
333
|
+
revert JBOmnichainDeployer_UnexpectedNFTReceived({caller: msg.sender, from: from, tokenId: tokenId});
|
|
334
|
+
}
|
|
332
335
|
|
|
333
336
|
return IERC721Receiver.onERC721Received.selector;
|
|
334
337
|
}
|
|
@@ -432,18 +435,19 @@ contract JBOmnichainDeployer is
|
|
|
432
435
|
cashOutTaxRate = context.cashOutTaxRate;
|
|
433
436
|
cashOutCount = context.cashOutCount;
|
|
434
437
|
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
438
|
+
// Start with local values.
|
|
439
|
+
totalSupply = context.totalSupply;
|
|
440
|
+
effectiveSurplusValue = context.surplus.value;
|
|
438
441
|
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
442
|
+
// If the ruleset aggregates cross-chain state, add remote supply and surplus.
|
|
443
|
+
if (!context.scopeCashOutsToLocalBalances) {
|
|
444
|
+
totalSupply += SUCKER_REGISTRY.remoteTotalSupplyOf(context.projectId);
|
|
445
|
+
effectiveSurplusValue += SUCKER_REGISTRY.remoteSurplusOf({
|
|
443
446
|
projectId: context.projectId,
|
|
444
447
|
decimals: context.surplus.decimals,
|
|
445
448
|
currency: uint256(context.surplus.currency)
|
|
446
449
|
});
|
|
450
|
+
}
|
|
447
451
|
|
|
448
452
|
// Will hold the 721 hook's cash out specifications (always 0 or 1 element).
|
|
449
453
|
JBCashOutHookSpecification[] memory tiered721HookSpecifications;
|
|
@@ -456,7 +460,6 @@ contract JBOmnichainDeployer is
|
|
|
456
460
|
// Forward to the 721 hook. It may change the tax rate, count, and return hook specs.
|
|
457
461
|
// Capture the 721 hook's totalSupply and effectiveSurplusValue — NFT cash-outs should use
|
|
458
462
|
// local-only denominators so holders reclaim against local surplus, not omnichain surplus.
|
|
459
|
-
// slither-disable-next-line unused-return
|
|
460
463
|
(cashOutTaxRate, cashOutCount, totalSupply, effectiveSurplusValue, tiered721HookSpecifications) =
|
|
461
464
|
IJBRulesetDataHook(address(tiered721Config.hook)).beforeCashOutRecordedWith(context);
|
|
462
465
|
}
|
|
@@ -483,10 +486,8 @@ contract JBOmnichainDeployer is
|
|
|
483
486
|
// (sum of tier prices), not fungible token counts. Letting the extra hook override
|
|
484
487
|
// cashOutCount would corrupt NFT pricing in the bonding curve.
|
|
485
488
|
if (address(tiered721Config.hook) != address(0) && tiered721Config.useDataHookForCashOut) {
|
|
486
|
-
// slither-disable-next-line unused-return
|
|
487
489
|
(cashOutTaxRate,,,, extraHookSpecifications) = extraHook.dataHook.beforeCashOutRecordedWith(hookContext);
|
|
488
490
|
} else {
|
|
489
|
-
// slither-disable-next-line unused-return
|
|
490
491
|
(cashOutTaxRate, cashOutCount,,, extraHookSpecifications) =
|
|
491
492
|
extraHook.dataHook.beforeCashOutRecordedWith(hookContext);
|
|
492
493
|
}
|
|
@@ -552,7 +553,6 @@ contract JBOmnichainDeployer is
|
|
|
552
553
|
has721Hook = true;
|
|
553
554
|
// Call the 721 hook directly — useDataHookForPay is always true for 721 hooks.
|
|
554
555
|
JBPayHookSpecification[] memory tiered721HookSpecs;
|
|
555
|
-
// slither-disable-next-line unused-return
|
|
556
556
|
(tiered721Weight, tiered721HookSpecs) =
|
|
557
557
|
IJBRulesetDataHook(address(tiered721Config.hook)).beforePayRecordedWith(context);
|
|
558
558
|
// The 721 hook returns a single spec (itself) whose amount is the total split amount.
|
|
@@ -750,7 +750,6 @@ contract JBOmnichainDeployer is
|
|
|
750
750
|
|
|
751
751
|
// Deploy a 721 hook and set up rulesets.
|
|
752
752
|
hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
|
|
753
|
-
// slither-disable-next-line reentrancy-benign
|
|
754
753
|
rulesetConfigurations = _setup721({
|
|
755
754
|
projectId: projectId,
|
|
756
755
|
rulesetConfigurations: rulesetConfigurations,
|
|
@@ -759,7 +758,6 @@ contract JBOmnichainDeployer is
|
|
|
759
758
|
});
|
|
760
759
|
|
|
761
760
|
// Launch the rulesets for the reserved project.
|
|
762
|
-
// slither-disable-start unused-return
|
|
763
761
|
controller.launchRulesetsFor({
|
|
764
762
|
projectId: projectId,
|
|
765
763
|
projectUri: projectUri,
|
|
@@ -767,14 +765,12 @@ contract JBOmnichainDeployer is
|
|
|
767
765
|
terminalConfigurations: terminalConfigurations,
|
|
768
766
|
memo: memo
|
|
769
767
|
});
|
|
770
|
-
// slither-disable-end unused-return
|
|
771
768
|
|
|
772
769
|
// Transfer the hook's ownership to the project (now that the project NFT has been minted).
|
|
773
770
|
JBOwnable(address(hook)).transferOwnershipToProject(projectId);
|
|
774
771
|
|
|
775
772
|
// Deploy the suckers (if applicable).
|
|
776
773
|
if (suckerDeploymentConfiguration.salt != bytes32(0)) {
|
|
777
|
-
// slither-disable-next-line unused-return
|
|
778
774
|
suckers = SUCKER_REGISTRY.deploySuckersFor({
|
|
779
775
|
projectId: projectId,
|
|
780
776
|
salt: keccak256(abi.encode(suckerDeploymentConfiguration.salt, _msgSender())),
|
|
@@ -820,7 +816,6 @@ contract JBOmnichainDeployer is
|
|
|
820
816
|
// Deploy a 721 hook, transfer its ownership to the project, and set up rulesets.
|
|
821
817
|
hook = _deploy721Hook({projectId: projectId, config: deploy721Config});
|
|
822
818
|
JBOwnable(address(hook)).transferOwnershipToProject(projectId);
|
|
823
|
-
// slither-disable-next-line reentrancy-benign
|
|
824
819
|
rulesetConfigurations = _setup721({
|
|
825
820
|
projectId: projectId,
|
|
826
821
|
rulesetConfigurations: rulesetConfigurations,
|
|
@@ -861,8 +856,11 @@ contract JBOmnichainDeployer is
|
|
|
861
856
|
// `block.timestamp + i` ruleset ID prediction incorrect.
|
|
862
857
|
uint256 latestRulesetId = controller.RULESETS().latestRulesetIdOf(projectId);
|
|
863
858
|
// forge-lint: disable-next-line(block-timestamp)
|
|
864
|
-
|
|
865
|
-
|
|
859
|
+
uint256 currentTimestamp = block.timestamp;
|
|
860
|
+
if (latestRulesetId >= currentTimestamp) {
|
|
861
|
+
revert JBOmnichainDeployer_RulesetIdsUnpredictable({
|
|
862
|
+
projectId: projectId, latestRulesetId: latestRulesetId, currentTimestamp: currentTimestamp
|
|
863
|
+
});
|
|
866
864
|
}
|
|
867
865
|
|
|
868
866
|
// Deploy a new 721 hook if tiers are provided, otherwise carry forward the existing hook.
|
|
@@ -899,12 +897,15 @@ contract JBOmnichainDeployer is
|
|
|
899
897
|
hook = previousConfig.hook;
|
|
900
898
|
// Revert if no hook exists to carry forward — this means no tiers were provided and
|
|
901
899
|
// no previous ruleset had a 721 hook deployed through this contract.
|
|
902
|
-
if (address(hook) == address(0))
|
|
900
|
+
if (address(hook) == address(0)) {
|
|
901
|
+
revert JBOmnichainDeployer_InvalidHook({
|
|
902
|
+
hook: address(hook), projectId: projectId, rulesetId: sourceRulesetId
|
|
903
|
+
});
|
|
904
|
+
}
|
|
903
905
|
// Preserve the previous ruleset's cash-out flag when carrying forward.
|
|
904
906
|
use721ForCashOut = previousConfig.useDataHookForCashOut;
|
|
905
907
|
}
|
|
906
908
|
|
|
907
|
-
// slither-disable-next-line reentrancy-benign
|
|
908
909
|
rulesetConfigurations = _setup721({
|
|
909
910
|
projectId: projectId,
|
|
910
911
|
rulesetConfigurations: rulesetConfigurations,
|
|
@@ -938,19 +939,23 @@ contract JBOmnichainDeployer is
|
|
|
938
939
|
returns (JBRulesetConfig[] memory)
|
|
939
940
|
{
|
|
940
941
|
for (uint256 i; i < rulesetConfigurations.length; i++) {
|
|
942
|
+
// forge-lint: disable-next-line(block-timestamp)
|
|
943
|
+
uint256 rulesetId = block.timestamp + i;
|
|
944
|
+
|
|
941
945
|
// Validate no self-reference.
|
|
942
|
-
if (rulesetConfigurations[i].metadata.dataHook == address(this))
|
|
946
|
+
if (rulesetConfigurations[i].metadata.dataHook == address(this)) {
|
|
947
|
+
revert JBOmnichainDeployer_InvalidHook({
|
|
948
|
+
hook: rulesetConfigurations[i].metadata.dataHook, projectId: projectId, rulesetId: rulesetId
|
|
949
|
+
});
|
|
950
|
+
}
|
|
943
951
|
|
|
944
952
|
// Store the 721 hook config per-ruleset.
|
|
945
|
-
|
|
946
|
-
// forge-lint: disable-next-line(block-timestamp)
|
|
947
|
-
_tiered721HookOf[projectId][block.timestamp + i] =
|
|
953
|
+
_tiered721HookOf[projectId][rulesetId] =
|
|
948
954
|
JBTiered721HookConfig({hook: hook721, useDataHookForCashOut: use721ForCashOut});
|
|
949
955
|
|
|
950
956
|
// Store custom hook from metadata (same as _setup).
|
|
951
957
|
if (rulesetConfigurations[i].metadata.dataHook != address(0)) {
|
|
952
|
-
|
|
953
|
-
_extraDataHookOf[projectId][block.timestamp + i] = JBDeployerHookConfig({
|
|
958
|
+
_extraDataHookOf[projectId][rulesetId] = JBDeployerHookConfig({
|
|
954
959
|
dataHook: IJBRulesetDataHook(rulesetConfigurations[i].metadata.dataHook),
|
|
955
960
|
useDataHookForPay: rulesetConfigurations[i].metadata.useDataHookForPay,
|
|
956
961
|
useDataHookForCashOut: rulesetConfigurations[i].metadata.useDataHookForCashOut
|
|
@@ -984,7 +989,11 @@ contract JBOmnichainDeployer is
|
|
|
984
989
|
pure
|
|
985
990
|
returns (JBOmnichain721Config memory config)
|
|
986
991
|
{
|
|
987
|
-
if (rulesetConfigurations.length == 0)
|
|
992
|
+
if (rulesetConfigurations.length == 0) {
|
|
993
|
+
revert JBOmnichainDeployer_NoRulesetConfigurations({
|
|
994
|
+
rulesetConfigurationCount: rulesetConfigurations.length
|
|
995
|
+
});
|
|
996
|
+
}
|
|
988
997
|
config.deployTiersHookConfig.tiersConfig.currency = rulesetConfigurations[0].metadata.baseCurrency;
|
|
989
998
|
config.deployTiersHookConfig.tiersConfig.decimals = 18;
|
|
990
999
|
}
|
|
@@ -1010,7 +1019,9 @@ contract JBOmnichainDeployer is
|
|
|
1010
1019
|
address current = address(DIRECTORY.controllerOf(projectId));
|
|
1011
1020
|
// Allow address(0) for fresh projects that haven't launched rulesets yet.
|
|
1012
1021
|
if (current != address(0) && current != address(controller)) {
|
|
1013
|
-
revert JBOmnichainDeployer_ControllerMismatch(
|
|
1022
|
+
revert JBOmnichainDeployer_ControllerMismatch({
|
|
1023
|
+
projectId: projectId, expectedController: current, actualController: address(controller)
|
|
1024
|
+
});
|
|
1014
1025
|
}
|
|
1015
1026
|
}
|
|
1016
1027
|
}
|