@rev-net/core-v6 0.0.11 → 0.0.12
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 +7 -7
- package/ARCHITECTURE.md +11 -11
- package/README.md +7 -4
- package/RISKS.md +2 -2
- package/SKILLS.md +8 -10
- package/STYLE_GUIDE.md +14 -1
- package/package.json +9 -9
- package/script/Deploy.s.sol +85 -35
- package/script/helpers/RevnetCoreDeploymentLib.sol +12 -5
- package/src/REVDeployer.sol +121 -129
- package/src/REVLoans.sol +14 -13
- package/src/interfaces/IREVDeployer.sol +25 -22
- package/src/structs/REVDeploy721TiersHookConfig.sol +12 -14
- package/test/REV.integrations.t.sol +17 -8
- package/test/REVAutoIssuanceFuzz.t.sol +9 -4
- package/test/REVDeployerRegressions.t.sol +13 -6
- package/test/REVInvincibility.t.sol +26 -12
- package/test/REVLifecycle.t.sol +9 -4
- package/test/REVLoans.invariants.t.sol +13 -6
- package/test/REVLoansAttacks.t.sol +12 -5
- package/test/REVLoansFeeRecovery.t.sol +12 -5
- package/test/REVLoansFindings.t.sol +16 -7
- package/test/REVLoansRegressions.t.sol +9 -4
- package/test/REVLoansSourced.t.sol +24 -11
- package/test/REVLoansUnSourced.t.sol +13 -6
- package/test/TestBurnHeldTokens.t.sol +16 -7
- package/test/TestCEIPattern.t.sol +12 -5
- package/test/TestCashOutCallerValidation.t.sol +12 -5
- package/test/TestConversionDocumentation.t.sol +25 -9
- package/test/TestCrossSourceReallocation.t.sol +12 -5
- package/test/TestEmptyBuybackSpecs.t.sol +23 -8
- package/test/TestFlashLoanSurplus.t.sol +12 -5
- package/test/TestHookArrayOOB.t.sol +19 -10
- package/test/TestLiquidationBehavior.t.sol +12 -5
- package/test/TestLowFindings.t.sol +16 -7
- package/test/TestMixedFixes.t.sol +16 -7
- package/test/TestSplitWeightAdjustment.t.sol +29 -12
- package/test/TestSplitWeightE2E.t.sol +24 -16
- package/test/TestSplitWeightFork.t.sol +20 -14
- package/test/TestStageTransitionBorrowable.t.sol +15 -5
- package/test/TestSwapTerminalPermission.t.sol +15 -5
- package/test/TestUint112Overflow.t.sol +12 -5
- package/test/TestZeroRepayment.t.sol +12 -5
- package/test/fork/ForkTestBase.sol +20 -14
- package/test/fork/TestCashOutFork.t.sol +8 -2
- package/test/fork/TestLoanCrossRulesetFork.t.sol +8 -2
- package/test/helpers/REVEmpty721Config.sol +45 -0
- package/test/regression/TestCumulativeLoanCounter.t.sol +12 -5
- package/test/regression/TestLiquidateGapHandling.t.sol +12 -5
package/src/REVDeployer.sol
CHANGED
|
@@ -276,7 +276,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
// Get a reference to the number of tokens being used to pay the fee (out of the total being cashed out).
|
|
279
|
-
uint256 feeCashOutCount = mulDiv(context.cashOutCount, FEE, JBConstants.MAX_FEE);
|
|
279
|
+
uint256 feeCashOutCount = mulDiv({x: context.cashOutCount, y: FEE, denominator: JBConstants.MAX_FEE});
|
|
280
280
|
uint256 nonFeeCashOutCount = context.cashOutCount - feeCashOutCount;
|
|
281
281
|
|
|
282
282
|
// Keep a reference to the amount claimable with non-fee tokens.
|
|
@@ -353,7 +353,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
353
353
|
if (projectAmount == 0) {
|
|
354
354
|
weight = 0;
|
|
355
355
|
} else if (projectAmount < context.amount.value) {
|
|
356
|
-
weight = mulDiv(weight, projectAmount, context.amount.value);
|
|
356
|
+
weight = mulDiv({x: weight, y: projectAmount, denominator: context.amount.value});
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
// Merge hook specifications: 721 hook spec first, then buyback hook spec.
|
|
@@ -565,7 +565,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
565
565
|
uint256[] memory customSplitOperatorPermissionIndexes = _extraOperatorPermissions[revnetId];
|
|
566
566
|
|
|
567
567
|
// Make the array that merges the default and custom operator permissions.
|
|
568
|
-
allOperatorPermissions = new uint256[](
|
|
568
|
+
allOperatorPermissions = new uint256[](9 + customSplitOperatorPermissionIndexes.length);
|
|
569
569
|
allOperatorPermissions[0] = JBPermissionIds.SET_SPLIT_GROUPS;
|
|
570
570
|
allOperatorPermissions[1] = JBPermissionIds.SET_BUYBACK_POOL;
|
|
571
571
|
allOperatorPermissions[2] = JBPermissionIds.SET_BUYBACK_TWAP;
|
|
@@ -574,10 +574,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
574
574
|
allOperatorPermissions[5] = JBPermissionIds.SUCKER_SAFETY;
|
|
575
575
|
allOperatorPermissions[6] = JBPermissionIds.SET_BUYBACK_HOOK;
|
|
576
576
|
allOperatorPermissions[7] = JBPermissionIds.SET_ROUTER_TERMINAL;
|
|
577
|
+
allOperatorPermissions[8] = JBPermissionIds.SET_TOKEN_METADATA;
|
|
577
578
|
|
|
578
579
|
// Copy the custom permissions into the array.
|
|
579
580
|
for (uint256 i; i < customSplitOperatorPermissionIndexes.length; i++) {
|
|
580
|
-
allOperatorPermissions[
|
|
581
|
+
allOperatorPermissions[9 + i] = customSplitOperatorPermissionIndexes[i];
|
|
581
582
|
}
|
|
582
583
|
}
|
|
583
584
|
|
|
@@ -691,16 +692,21 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
691
692
|
/// @param terminalConfigurations The terminals to set up for the revnet. Used for payments and cash outs.
|
|
692
693
|
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet. Suckers facilitate cross-chain
|
|
693
694
|
/// token transfers between peer revnets on different networks.
|
|
695
|
+
/// @param tiered721HookConfiguration How to set up the tiered ERC-721 hook for the revnet.
|
|
696
|
+
/// @param allowedPosts Restrictions on which croptop posts are allowed on the revnet's ERC-721 tiers.
|
|
694
697
|
/// @return revnetId The ID of the newly created revnet.
|
|
698
|
+
/// @return hook The address of the tiered ERC-721 hook that was deployed for the revnet.
|
|
695
699
|
function deployFor(
|
|
696
700
|
uint256 revnetId,
|
|
697
701
|
REVConfig calldata configuration,
|
|
698
702
|
JBTerminalConfig[] calldata terminalConfigurations,
|
|
699
|
-
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
703
|
+
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
|
|
704
|
+
REVDeploy721TiersHookConfig calldata tiered721HookConfiguration,
|
|
705
|
+
REVCroptopAllowedPost[] calldata allowedPosts
|
|
700
706
|
)
|
|
701
707
|
external
|
|
702
708
|
override
|
|
703
|
-
returns (uint256)
|
|
709
|
+
returns (uint256, IJB721TiersHook hook)
|
|
704
710
|
{
|
|
705
711
|
// Keep a reference to the revnet ID which was passed in.
|
|
706
712
|
bool shouldDeployNewRevnet = revnetId == 0;
|
|
@@ -709,23 +715,69 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
709
715
|
// (which will be 1 greater than the current count).
|
|
710
716
|
if (shouldDeployNewRevnet) revnetId = _nextProjectId();
|
|
711
717
|
|
|
712
|
-
//
|
|
713
|
-
|
|
714
|
-
revnetId: revnetId,
|
|
718
|
+
// Deploy the revnet with the specified tiered ERC-721 hook and croptop posting criteria.
|
|
719
|
+
hook = _deploy721RevnetFor({
|
|
720
|
+
revnetId: revnetId,
|
|
721
|
+
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
722
|
+
configuration: configuration,
|
|
723
|
+
terminalConfigurations: terminalConfigurations,
|
|
724
|
+
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
725
|
+
tiered721HookConfiguration: tiered721HookConfiguration,
|
|
726
|
+
allowedPosts: allowedPosts
|
|
715
727
|
});
|
|
716
728
|
|
|
717
|
-
|
|
718
|
-
|
|
729
|
+
return (revnetId, hook);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/// @inheritdoc IREVDeployer
|
|
733
|
+
function deployFor(
|
|
734
|
+
uint256 revnetId,
|
|
735
|
+
REVConfig calldata configuration,
|
|
736
|
+
JBTerminalConfig[] calldata terminalConfigurations,
|
|
737
|
+
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
738
|
+
)
|
|
739
|
+
external
|
|
740
|
+
override
|
|
741
|
+
returns (uint256, IJB721TiersHook hook)
|
|
742
|
+
{
|
|
743
|
+
bool shouldDeployNewRevnet = revnetId == 0;
|
|
744
|
+
if (shouldDeployNewRevnet) revnetId = _nextProjectId();
|
|
745
|
+
|
|
746
|
+
// Deploy the revnet (project, rulesets, ERC-20, suckers, etc.).
|
|
747
|
+
bytes32 encodedConfigurationHash = _deployRevnetFor({
|
|
719
748
|
revnetId: revnetId,
|
|
720
749
|
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
721
750
|
configuration: configuration,
|
|
722
751
|
terminalConfigurations: terminalConfigurations,
|
|
723
|
-
suckerDeploymentConfiguration: suckerDeploymentConfiguration
|
|
724
|
-
rulesetConfigurations: rulesetConfigurations,
|
|
725
|
-
encodedConfigurationHash: encodedConfigurationHash
|
|
752
|
+
suckerDeploymentConfiguration: suckerDeploymentConfiguration
|
|
726
753
|
});
|
|
727
754
|
|
|
728
|
-
|
|
755
|
+
// Deploy a default empty 721 hook for the revnet.
|
|
756
|
+
{
|
|
757
|
+
JBDeploy721TiersHookConfig memory deployConfig;
|
|
758
|
+
deployConfig.tiersConfig.currency = configuration.baseCurrency;
|
|
759
|
+
deployConfig.tiersConfig.decimals = 18;
|
|
760
|
+
|
|
761
|
+
hook = HOOK_DEPLOYER.deployHookFor({
|
|
762
|
+
projectId: revnetId,
|
|
763
|
+
deployTiersHookConfig: deployConfig,
|
|
764
|
+
salt: keccak256(abi.encode(bytes32(0), encodedConfigurationHash, _msgSender()))
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Store the tiered ERC-721 hook.
|
|
769
|
+
tiered721HookOf[revnetId] = hook;
|
|
770
|
+
|
|
771
|
+
// Grant the split operator all 721 permissions (no prevent* flags for default config).
|
|
772
|
+
_extraOperatorPermissions[revnetId].push(JBPermissionIds.ADJUST_721_TIERS);
|
|
773
|
+
_extraOperatorPermissions[revnetId].push(JBPermissionIds.SET_721_METADATA);
|
|
774
|
+
_extraOperatorPermissions[revnetId].push(JBPermissionIds.MINT_721);
|
|
775
|
+
_extraOperatorPermissions[revnetId].push(JBPermissionIds.SET_721_DISCOUNT_PERCENT);
|
|
776
|
+
|
|
777
|
+
// Give the split operator their permissions (base + 721 extras).
|
|
778
|
+
_setSplitOperatorOf({revnetId: revnetId, operator: configuration.splitOperator});
|
|
779
|
+
|
|
780
|
+
return (revnetId, hook);
|
|
729
781
|
}
|
|
730
782
|
|
|
731
783
|
/// @notice Burn any of a revnet's tokens held by this contract.
|
|
@@ -775,51 +827,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
775
827
|
});
|
|
776
828
|
}
|
|
777
829
|
|
|
778
|
-
/// @notice Launch a revnet which sells tiered ERC-721s and (optionally) allows croptop posts to its ERC-721 tiers.
|
|
779
|
-
/// @dev When initializing an existing project (revnetId != 0), the project must be blank (no controller or
|
|
780
|
-
/// rulesets). The initialization is irreversible. See `deployFor` documentation for full details.
|
|
781
|
-
/// @param revnetId The ID of the Juicebox project to initialize as a revnet. Send 0 to deploy a new revnet.
|
|
782
|
-
/// @param configuration Core revnet configuration. See `REVConfig`.
|
|
783
|
-
/// @param terminalConfigurations The terminals to set up for the revnet. Used for payments and cash outs.
|
|
784
|
-
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet. Suckers facilitate cross-chain
|
|
785
|
-
/// token transfers between peer revnets on different networks.
|
|
786
|
-
/// @param tiered721HookConfiguration How to set up the tiered ERC-721 hook for the revnet.
|
|
787
|
-
/// @param allowedPosts Restrictions on which croptop posts are allowed on the revnet's ERC-721 tiers.
|
|
788
|
-
/// @return revnetId The ID of the newly created revnet.
|
|
789
|
-
/// @return hook The address of the tiered ERC-721 hook that was deployed for the revnet.
|
|
790
|
-
function deployWith721sFor(
|
|
791
|
-
uint256 revnetId,
|
|
792
|
-
REVConfig calldata configuration,
|
|
793
|
-
JBTerminalConfig[] calldata terminalConfigurations,
|
|
794
|
-
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
|
|
795
|
-
REVDeploy721TiersHookConfig calldata tiered721HookConfiguration,
|
|
796
|
-
REVCroptopAllowedPost[] calldata allowedPosts
|
|
797
|
-
)
|
|
798
|
-
external
|
|
799
|
-
override
|
|
800
|
-
returns (uint256, IJB721TiersHook hook)
|
|
801
|
-
{
|
|
802
|
-
// Keep a reference to the revnet ID which was passed in.
|
|
803
|
-
bool shouldDeployNewRevnet = revnetId == 0;
|
|
804
|
-
|
|
805
|
-
// If the caller is deploying a new revnet, calculate its ID
|
|
806
|
-
// (which will be 1 greater than the current count).
|
|
807
|
-
if (shouldDeployNewRevnet) revnetId = _nextProjectId();
|
|
808
|
-
|
|
809
|
-
// Deploy the revnet with the specified tiered ERC-721 hook and croptop posting criteria.
|
|
810
|
-
hook = _deploy721RevnetFor({
|
|
811
|
-
revnetId: revnetId,
|
|
812
|
-
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
813
|
-
configuration: configuration,
|
|
814
|
-
terminalConfigurations: terminalConfigurations,
|
|
815
|
-
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
816
|
-
tiered721HookConfiguration: tiered721HookConfiguration,
|
|
817
|
-
allowedPosts: allowedPosts
|
|
818
|
-
});
|
|
819
|
-
|
|
820
|
-
return (revnetId, hook);
|
|
821
|
-
}
|
|
822
|
-
|
|
823
830
|
/// @notice Change a revnet's split operator.
|
|
824
831
|
/// @dev Only a revnet's current split operator can set a new split operator.
|
|
825
832
|
/// @param revnetId The ID of the revnet to set the split operator of.
|
|
@@ -852,91 +859,86 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
852
859
|
function _beforeTransferTo(address to, address token, uint256 amount) internal returns (uint256) {
|
|
853
860
|
// If the token is the native token, no allowance needed.
|
|
854
861
|
if (token == JBConstants.NATIVE_TOKEN) return amount;
|
|
855
|
-
IERC20(token).safeIncreaseAllowance(to, amount);
|
|
862
|
+
IERC20(token).safeIncreaseAllowance({spender: to, value: amount});
|
|
856
863
|
return 0;
|
|
857
864
|
}
|
|
858
865
|
|
|
859
866
|
/// @notice Deploy a revnet which sells tiered ERC-721s and (optionally) allows croptop posts to its ERC-721 tiers.
|
|
860
|
-
/// @param revnetId The ID of the Juicebox project to turn into a revnet. Send 0 to deploy a new revnet.
|
|
861
|
-
/// @param shouldDeployNewRevnet Whether to deploy a new revnet or convert an existing Juicebox project into a
|
|
862
|
-
/// revnet.
|
|
863
|
-
/// @param configuration Core revnet configuration. See `REVConfig`.
|
|
864
|
-
/// @param terminalConfigurations The terminals to set up for the revnet. Used for payments and cash outs.
|
|
865
|
-
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet. Suckers facilitate cross-chain
|
|
866
|
-
/// token transfers between peer revnets on different networks.
|
|
867
|
-
/// @param tiered721HookConfiguration How to set up the tiered ERC-721 hook for the revnet.
|
|
868
|
-
/// @param allowedPosts Restrictions on which croptop posts are allowed on the revnet's ERC-721 tiers.
|
|
869
|
-
/// @return hook The address of the tiered ERC-721 hook that was deployed for the revnet.
|
|
870
867
|
function _deploy721RevnetFor(
|
|
871
868
|
uint256 revnetId,
|
|
872
869
|
bool shouldDeployNewRevnet,
|
|
873
870
|
REVConfig calldata configuration,
|
|
874
871
|
JBTerminalConfig[] calldata terminalConfigurations,
|
|
875
872
|
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration,
|
|
876
|
-
REVDeploy721TiersHookConfig
|
|
877
|
-
REVCroptopAllowedPost[]
|
|
873
|
+
REVDeploy721TiersHookConfig memory tiered721HookConfiguration,
|
|
874
|
+
REVCroptopAllowedPost[] memory allowedPosts
|
|
878
875
|
)
|
|
879
876
|
internal
|
|
880
877
|
returns (IJB721TiersHook hook)
|
|
881
878
|
{
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
revnetId: revnetId,
|
|
879
|
+
// Deploy the revnet (project, rulesets, ERC-20, suckers, etc.).
|
|
880
|
+
bytes32 encodedConfigurationHash = _deployRevnetFor({
|
|
881
|
+
revnetId: revnetId,
|
|
882
|
+
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
883
|
+
configuration: configuration,
|
|
884
|
+
terminalConfigurations: terminalConfigurations,
|
|
885
|
+
suckerDeploymentConfiguration: suckerDeploymentConfiguration
|
|
885
886
|
});
|
|
886
887
|
|
|
887
888
|
// Convert the REVBaseline721HookConfig to JBDeploy721TiersHookConfig, forcing issueTokensForSplits to false.
|
|
888
889
|
// Revnets do their own weight adjustment for splits, so the 721 hook must not also adjust.
|
|
889
|
-
JBDeploy721TiersHookConfig memory hookConfig = JBDeploy721TiersHookConfig({
|
|
890
|
-
name: tiered721HookConfiguration.baseline721HookConfiguration.name,
|
|
891
|
-
symbol: tiered721HookConfiguration.baseline721HookConfiguration.symbol,
|
|
892
|
-
baseUri: tiered721HookConfiguration.baseline721HookConfiguration.baseUri,
|
|
893
|
-
tokenUriResolver: tiered721HookConfiguration.baseline721HookConfiguration.tokenUriResolver,
|
|
894
|
-
contractUri: tiered721HookConfiguration.baseline721HookConfiguration.contractUri,
|
|
895
|
-
tiersConfig: tiered721HookConfiguration.baseline721HookConfiguration.tiersConfig,
|
|
896
|
-
reserveBeneficiary: tiered721HookConfiguration.baseline721HookConfiguration.reserveBeneficiary,
|
|
897
|
-
flags: JB721TiersHookFlags({
|
|
898
|
-
noNewTiersWithReserves: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
899
|
-
.noNewTiersWithReserves,
|
|
900
|
-
noNewTiersWithVotes: tiered721HookConfiguration.baseline721HookConfiguration.flags.noNewTiersWithVotes,
|
|
901
|
-
noNewTiersWithOwnerMinting: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
902
|
-
.noNewTiersWithOwnerMinting,
|
|
903
|
-
preventOverspending: tiered721HookConfiguration.baseline721HookConfiguration.flags.preventOverspending,
|
|
904
|
-
issueTokensForSplits: false
|
|
905
|
-
})
|
|
906
|
-
});
|
|
907
|
-
|
|
908
|
-
// Deploy the tiered ERC-721 hook contract.
|
|
909
|
-
// slither-disable-next-line reentrancy-benign
|
|
910
890
|
hook = HOOK_DEPLOYER.deployHookFor({
|
|
911
891
|
projectId: revnetId,
|
|
912
|
-
deployTiersHookConfig:
|
|
892
|
+
deployTiersHookConfig: JBDeploy721TiersHookConfig({
|
|
893
|
+
name: tiered721HookConfiguration.baseline721HookConfiguration.name,
|
|
894
|
+
symbol: tiered721HookConfiguration.baseline721HookConfiguration.symbol,
|
|
895
|
+
baseUri: tiered721HookConfiguration.baseline721HookConfiguration.baseUri,
|
|
896
|
+
tokenUriResolver: tiered721HookConfiguration.baseline721HookConfiguration.tokenUriResolver,
|
|
897
|
+
contractUri: tiered721HookConfiguration.baseline721HookConfiguration.contractUri,
|
|
898
|
+
tiersConfig: tiered721HookConfiguration.baseline721HookConfiguration.tiersConfig,
|
|
899
|
+
reserveBeneficiary: tiered721HookConfiguration.baseline721HookConfiguration.reserveBeneficiary,
|
|
900
|
+
flags: JB721TiersHookFlags({
|
|
901
|
+
noNewTiersWithReserves: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
902
|
+
.noNewTiersWithReserves,
|
|
903
|
+
noNewTiersWithVotes: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
904
|
+
.noNewTiersWithVotes,
|
|
905
|
+
noNewTiersWithOwnerMinting: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
906
|
+
.noNewTiersWithOwnerMinting,
|
|
907
|
+
preventOverspending: tiered721HookConfiguration.baseline721HookConfiguration.flags
|
|
908
|
+
.preventOverspending,
|
|
909
|
+
issueTokensForSplits: false
|
|
910
|
+
})
|
|
911
|
+
}),
|
|
913
912
|
salt: keccak256(abi.encode(tiered721HookConfiguration.salt, encodedConfigurationHash, _msgSender()))
|
|
914
913
|
});
|
|
915
914
|
|
|
916
915
|
// Store the tiered ERC-721 hook.
|
|
917
916
|
tiered721HookOf[revnetId] = hook;
|
|
918
917
|
|
|
919
|
-
//
|
|
920
|
-
if (tiered721HookConfiguration.
|
|
918
|
+
// Give the split operator permission to add and remove tiers unless prevented.
|
|
919
|
+
if (!tiered721HookConfiguration.preventSplitOperatorAdjustingTiers) {
|
|
921
920
|
_extraOperatorPermissions[revnetId].push(JBPermissionIds.ADJUST_721_TIERS);
|
|
922
921
|
}
|
|
923
922
|
|
|
924
|
-
//
|
|
925
|
-
if (tiered721HookConfiguration.
|
|
923
|
+
// Give the split operator permission to set ERC-721 tier metadata unless prevented.
|
|
924
|
+
if (!tiered721HookConfiguration.preventSplitOperatorUpdatingMetadata) {
|
|
926
925
|
_extraOperatorPermissions[revnetId].push(JBPermissionIds.SET_721_METADATA);
|
|
927
926
|
}
|
|
928
927
|
|
|
929
|
-
//
|
|
930
|
-
// from tiers with `allowOwnerMint` set to true.
|
|
931
|
-
if (tiered721HookConfiguration.
|
|
928
|
+
// Give the split operator permission to mint ERC-721s (without a payment)
|
|
929
|
+
// from tiers with `allowOwnerMint` set to true, unless prevented.
|
|
930
|
+
if (!tiered721HookConfiguration.preventSplitOperatorMinting) {
|
|
932
931
|
_extraOperatorPermissions[revnetId].push(JBPermissionIds.MINT_721);
|
|
933
932
|
}
|
|
934
933
|
|
|
935
|
-
//
|
|
936
|
-
if (tiered721HookConfiguration.
|
|
934
|
+
// Give the split operator permission to increase the discount of a tier unless prevented.
|
|
935
|
+
if (!tiered721HookConfiguration.preventSplitOperatorIncreasingDiscountPercent) {
|
|
937
936
|
_extraOperatorPermissions[revnetId].push(JBPermissionIds.SET_721_DISCOUNT_PERCENT);
|
|
938
937
|
}
|
|
939
938
|
|
|
939
|
+
// Give the split operator their permissions (base + 721 extras).
|
|
940
|
+
_setSplitOperatorOf({revnetId: revnetId, operator: configuration.splitOperator});
|
|
941
|
+
|
|
940
942
|
// If there are posts to allow, configure them.
|
|
941
943
|
if (allowedPosts.length != 0) {
|
|
942
944
|
// Keep a reference to the formatted allowed posts.
|
|
@@ -945,7 +947,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
945
947
|
// Iterate through each post to add it to the formatted list.
|
|
946
948
|
for (uint256 i; i < allowedPosts.length; i++) {
|
|
947
949
|
// Set the post being iterated on.
|
|
948
|
-
REVCroptopAllowedPost
|
|
950
|
+
REVCroptopAllowedPost memory post = allowedPosts[i];
|
|
949
951
|
|
|
950
952
|
// Set the formatted post.
|
|
951
953
|
formattedAllowedPosts[i] = CTAllowedPost({
|
|
@@ -967,16 +969,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
967
969
|
operator: address(PUBLISHER), revnetId: revnetId, permissionId: JBPermissionIds.ADJUST_721_TIERS
|
|
968
970
|
});
|
|
969
971
|
}
|
|
970
|
-
|
|
971
|
-
_deployRevnetFor({
|
|
972
|
-
revnetId: revnetId,
|
|
973
|
-
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
974
|
-
configuration: configuration,
|
|
975
|
-
terminalConfigurations: terminalConfigurations,
|
|
976
|
-
suckerDeploymentConfiguration: suckerDeploymentConfiguration,
|
|
977
|
-
rulesetConfigurations: rulesetConfigurations,
|
|
978
|
-
encodedConfigurationHash: encodedConfigurationHash
|
|
979
|
-
});
|
|
980
972
|
}
|
|
981
973
|
|
|
982
974
|
/// @notice Deploy a revnet, or initialize an existing Juicebox project as a revnet.
|
|
@@ -993,21 +985,22 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
993
985
|
/// @param terminalConfigurations The terminals to set up for the revnet. Used for payments and cash outs.
|
|
994
986
|
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet. Suckers facilitate cross-chain
|
|
995
987
|
/// token transfers between peer revnets on different networks.
|
|
996
|
-
/// @
|
|
997
|
-
/// @param encodedConfigurationHash A hash that represents the revnet's configuration.
|
|
998
|
-
/// See `_makeRulesetConfigurations(…)` for encoding details. Clients can read the encoded configuration
|
|
999
|
-
/// from the `DeployRevnet` event emitted by this contract.
|
|
988
|
+
/// @return encodedConfigurationHash A hash that represents the revnet's configuration.
|
|
1000
989
|
function _deployRevnetFor(
|
|
1001
990
|
uint256 revnetId,
|
|
1002
991
|
bool shouldDeployNewRevnet,
|
|
1003
992
|
REVConfig calldata configuration,
|
|
1004
993
|
JBTerminalConfig[] calldata terminalConfigurations,
|
|
1005
|
-
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
1006
|
-
JBRulesetConfig[] memory rulesetConfigurations,
|
|
1007
|
-
bytes32 encodedConfigurationHash
|
|
994
|
+
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
1008
995
|
)
|
|
1009
996
|
internal
|
|
997
|
+
returns (bytes32 encodedConfigurationHash)
|
|
1010
998
|
{
|
|
999
|
+
// Normalize and encode the configurations.
|
|
1000
|
+
JBRulesetConfig[] memory rulesetConfigurations;
|
|
1001
|
+
(rulesetConfigurations, encodedConfigurationHash) = _makeRulesetConfigurations({
|
|
1002
|
+
revnetId: revnetId, configuration: configuration, terminalConfigurations: terminalConfigurations
|
|
1003
|
+
});
|
|
1011
1004
|
if (shouldDeployNewRevnet) {
|
|
1012
1005
|
// If we're deploying a new revnet, launch a Juicebox project for it.
|
|
1013
1006
|
// Sanity check that we deployed the `revnetId` that we expected to deploy.
|
|
@@ -1064,13 +1057,12 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IJBRulesetDataHook, IJBCas
|
|
|
1064
1057
|
JBTerminalConfig calldata terminalConfiguration = terminalConfigurations[i];
|
|
1065
1058
|
for (uint256 j; j < terminalConfiguration.accountingContextsToAccept.length; j++) {
|
|
1066
1059
|
// slither-disable-next-line calls-loop
|
|
1067
|
-
_tryInitializeBuybackPoolFor(
|
|
1060
|
+
_tryInitializeBuybackPoolFor({
|
|
1061
|
+
revnetId: revnetId, terminalToken: terminalConfiguration.accountingContextsToAccept[j].token
|
|
1062
|
+
});
|
|
1068
1063
|
}
|
|
1069
1064
|
}
|
|
1070
1065
|
|
|
1071
|
-
// Give the split operator their permissions.
|
|
1072
|
-
_setSplitOperatorOf({revnetId: revnetId, operator: configuration.splitOperator});
|
|
1073
|
-
|
|
1074
1066
|
// Deploy the suckers (if applicable).
|
|
1075
1067
|
if (suckerDeploymentConfiguration.salt != bytes32(0)) {
|
|
1076
1068
|
_deploySuckersFor({
|
package/src/REVLoans.sol
CHANGED
|
@@ -255,7 +255,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
255
255
|
/// @param amount The amount being paid off.
|
|
256
256
|
/// @return sourceFeeAmount The source fee amount for the loan.
|
|
257
257
|
function determineSourceFeeAmount(REVLoan memory loan, uint256 amount) public view returns (uint256) {
|
|
258
|
-
return _determineSourceFeeAmount(loan, amount);
|
|
258
|
+
return _determineSourceFeeAmount({loan: loan, amount: amount});
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
/// @notice The revnet ID for the loan with the provided loan ID.
|
|
@@ -426,15 +426,15 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
426
426
|
|
|
427
427
|
uint256 fullSourceFeeAmount = JBFees.feeAmountFrom({
|
|
428
428
|
amountBeforeFee: loan.amount - prepaid,
|
|
429
|
-
feePercent: mulDiv(
|
|
430
|
-
timeSinceLoanCreated - loan.prepaidDuration,
|
|
431
|
-
JBConstants.MAX_FEE,
|
|
432
|
-
LOAN_LIQUIDATION_DURATION - loan.prepaidDuration
|
|
433
|
-
)
|
|
429
|
+
feePercent: mulDiv({
|
|
430
|
+
x: timeSinceLoanCreated - loan.prepaidDuration,
|
|
431
|
+
y: JBConstants.MAX_FEE,
|
|
432
|
+
denominator: LOAN_LIQUIDATION_DURATION - loan.prepaidDuration
|
|
433
|
+
})
|
|
434
434
|
});
|
|
435
435
|
|
|
436
436
|
// Calculate the source fee amount for the amount being paid off.
|
|
437
|
-
return mulDiv(fullSourceFeeAmount, amount, loan.amount);
|
|
437
|
+
return mulDiv({x: fullSourceFeeAmount, y: amount, denominator: loan.amount});
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
/// @notice Generate a ID for a loan given a revnet ID and a loan number within that revnet.
|
|
@@ -519,7 +519,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
519
519
|
decimals: decimals
|
|
520
520
|
});
|
|
521
521
|
|
|
522
|
-
borrowedAmount += mulDiv(normalizedTokens, 10 ** decimals, pricePerUnit);
|
|
522
|
+
borrowedAmount += mulDiv({x: normalizedTokens, y: 10 ** decimals, denominator: pricePerUnit});
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
525
|
}
|
|
@@ -558,7 +558,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
558
558
|
if (collateralCount == 0) revert REVLoans_ZeroCollateralLoanIsInvalid();
|
|
559
559
|
|
|
560
560
|
// Make sure the source terminal is registered in the directory for this revnet.
|
|
561
|
-
if (!DIRECTORY.isTerminalOf(revnetId, IJBTerminal(address(source.terminal)))) {
|
|
561
|
+
if (!DIRECTORY.isTerminalOf({projectId: revnetId, terminal: IJBTerminal(address(source.terminal))})) {
|
|
562
562
|
revert REVLoans_InvalidTerminal(address(source.terminal), revnetId);
|
|
563
563
|
}
|
|
564
564
|
|
|
@@ -581,7 +581,8 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
581
581
|
loan.source = source;
|
|
582
582
|
loan.createdAt = uint40(block.timestamp);
|
|
583
583
|
loan.prepaidFeePercent = uint16(prepaidFeePercent);
|
|
584
|
-
loan.prepaidDuration =
|
|
584
|
+
loan.prepaidDuration =
|
|
585
|
+
uint32(mulDiv({x: prepaidFeePercent, y: LOAN_LIQUIDATION_DURATION, denominator: MAX_PREPAID_FEE_PERCENT}));
|
|
585
586
|
|
|
586
587
|
// Get the amount of the loan.
|
|
587
588
|
uint256 borrowAmount = _borrowAmountFrom({loan: loan, revnetId: revnetId, collateralCount: collateralCount});
|
|
@@ -794,7 +795,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
794
795
|
if (repayBorrowAmount == 0 && collateralCountToReturn == 0) revert REVLoans_NothingToRepay();
|
|
795
796
|
|
|
796
797
|
// Keep a reference to the fee that'll be taken.
|
|
797
|
-
uint256 sourceFeeAmount = _determineSourceFeeAmount(loan, repayBorrowAmount);
|
|
798
|
+
uint256 sourceFeeAmount = _determineSourceFeeAmount({loan: loan, amount: repayBorrowAmount});
|
|
798
799
|
|
|
799
800
|
// Add the fee to the repay amount.
|
|
800
801
|
repayBorrowAmount += sourceFeeAmount;
|
|
@@ -1093,7 +1094,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1093
1094
|
function _beforeTransferTo(address to, address token, uint256 amount) internal returns (uint256) {
|
|
1094
1095
|
// If the token is the native token, no allowance needed.
|
|
1095
1096
|
if (token == JBConstants.NATIVE_TOKEN) return amount;
|
|
1096
|
-
IERC20(token).safeIncreaseAllowance(to, amount);
|
|
1097
|
+
IERC20(token).safeIncreaseAllowance({spender: to, value: amount});
|
|
1097
1098
|
return 0;
|
|
1098
1099
|
}
|
|
1099
1100
|
|
|
@@ -1342,7 +1343,7 @@ contract REVLoans is ERC721, ERC2771Context, Ownable, IREVLoans {
|
|
|
1342
1343
|
}
|
|
1343
1344
|
|
|
1344
1345
|
// If there's sufficient approval, transfer normally.
|
|
1345
|
-
if (IERC20(token).allowance(address(from), address(this)) >= amount) {
|
|
1346
|
+
if (IERC20(token).allowance({owner: address(from), spender: address(this)}) >= amount) {
|
|
1346
1347
|
return IERC20(token).safeTransferFrom({from: from, to: to, value: amount});
|
|
1347
1348
|
}
|
|
1348
1349
|
|
|
@@ -182,52 +182,55 @@ interface IREVDeployer {
|
|
|
182
182
|
/// @param revnetId The ID of the revnet whose tokens should be burned.
|
|
183
183
|
function burnHeldTokensOf(uint256 revnetId) external;
|
|
184
184
|
|
|
185
|
-
/// @notice Deploy a revnet
|
|
185
|
+
/// @notice Deploy a revnet with a tiered ERC-721 hook and optional croptop posting support.
|
|
186
|
+
/// @dev Every revnet gets a 721 hook — pass an empty config if no tiers are needed initially.
|
|
186
187
|
/// @param revnetId The ID of the Juicebox project to initialize. Send 0 to deploy a new revnet.
|
|
187
188
|
/// @param configuration Core revnet configuration.
|
|
188
189
|
/// @param terminalConfigurations The terminals to set up for the revnet.
|
|
189
190
|
/// @param suckerDeploymentConfiguration The suckers to set up for cross-chain token transfers.
|
|
191
|
+
/// @param tiered721HookConfiguration How to set up the tiered ERC-721 hook for the revnet.
|
|
192
|
+
/// @param allowedPosts Restrictions on which croptop posts are allowed on the revnet's ERC-721 tiers.
|
|
190
193
|
/// @return The ID of the newly created or initialized revnet.
|
|
194
|
+
/// @return hook The tiered ERC-721 hook that was deployed for the revnet.
|
|
191
195
|
function deployFor(
|
|
192
196
|
uint256 revnetId,
|
|
193
197
|
REVConfig memory configuration,
|
|
194
198
|
JBTerminalConfig[] memory terminalConfigurations,
|
|
195
|
-
REVSuckerDeploymentConfig memory suckerDeploymentConfiguration
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
returns (uint256);
|
|
199
|
-
|
|
200
|
-
/// @notice Deploy new suckers for an existing revnet.
|
|
201
|
-
/// @param revnetId The ID of the revnet to deploy suckers for.
|
|
202
|
-
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet.
|
|
203
|
-
/// @return suckers The addresses of the deployed suckers.
|
|
204
|
-
function deploySuckersFor(
|
|
205
|
-
uint256 revnetId,
|
|
206
|
-
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
199
|
+
REVSuckerDeploymentConfig memory suckerDeploymentConfiguration,
|
|
200
|
+
REVDeploy721TiersHookConfig memory tiered721HookConfiguration,
|
|
201
|
+
REVCroptopAllowedPost[] memory allowedPosts
|
|
207
202
|
)
|
|
208
203
|
external
|
|
209
|
-
returns (
|
|
204
|
+
returns (uint256, IJB721TiersHook hook);
|
|
210
205
|
|
|
211
|
-
/// @notice Deploy a revnet with tiered ERC-
|
|
206
|
+
/// @notice Deploy a revnet with a default empty tiered ERC-721 hook.
|
|
207
|
+
/// @dev Convenience overload — constructs an empty 721 config internally and delegates to the 6-arg version.
|
|
212
208
|
/// @param revnetId The ID of the Juicebox project to initialize. Send 0 to deploy a new revnet.
|
|
213
209
|
/// @param configuration Core revnet configuration.
|
|
214
210
|
/// @param terminalConfigurations The terminals to set up for the revnet.
|
|
215
211
|
/// @param suckerDeploymentConfiguration The suckers to set up for cross-chain token transfers.
|
|
216
|
-
/// @param tiered721HookConfiguration How to set up the tiered ERC-721 hook.
|
|
217
|
-
/// @param allowedPosts Restrictions on which croptop posts are allowed on the revnet's ERC-721 tiers.
|
|
218
212
|
/// @return The ID of the newly created or initialized revnet.
|
|
219
213
|
/// @return hook The tiered ERC-721 hook that was deployed for the revnet.
|
|
220
|
-
function
|
|
214
|
+
function deployFor(
|
|
221
215
|
uint256 revnetId,
|
|
222
|
-
REVConfig
|
|
216
|
+
REVConfig memory configuration,
|
|
223
217
|
JBTerminalConfig[] memory terminalConfigurations,
|
|
224
|
-
REVSuckerDeploymentConfig memory suckerDeploymentConfiguration
|
|
225
|
-
REVDeploy721TiersHookConfig memory tiered721HookConfiguration,
|
|
226
|
-
REVCroptopAllowedPost[] memory allowedPosts
|
|
218
|
+
REVSuckerDeploymentConfig memory suckerDeploymentConfiguration
|
|
227
219
|
)
|
|
228
220
|
external
|
|
229
221
|
returns (uint256, IJB721TiersHook hook);
|
|
230
222
|
|
|
223
|
+
/// @notice Deploy new suckers for an existing revnet.
|
|
224
|
+
/// @param revnetId The ID of the revnet to deploy suckers for.
|
|
225
|
+
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet.
|
|
226
|
+
/// @return suckers The addresses of the deployed suckers.
|
|
227
|
+
function deploySuckersFor(
|
|
228
|
+
uint256 revnetId,
|
|
229
|
+
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
230
|
+
)
|
|
231
|
+
external
|
|
232
|
+
returns (address[] memory suckers);
|
|
233
|
+
|
|
231
234
|
/// @notice Change a revnet's split operator. Only the current split operator can call this.
|
|
232
235
|
/// @param revnetId The ID of the revnet.
|
|
233
236
|
/// @param newSplitOperator The new split operator's address.
|
|
@@ -5,21 +5,19 @@ import {REVBaseline721HookConfig} from "./REVBaseline721HookConfig.sol";
|
|
|
5
5
|
|
|
6
6
|
/// @custom:member baseline721HookConfiguration The baseline config.
|
|
7
7
|
/// @custom:member salt The salt to base the collection's address on.
|
|
8
|
-
/// @custom:member
|
|
9
|
-
/// tiers
|
|
10
|
-
/// the
|
|
11
|
-
///
|
|
12
|
-
///
|
|
13
|
-
///
|
|
14
|
-
///
|
|
15
|
-
///
|
|
16
|
-
/// the
|
|
17
|
-
/// discount of a tier.
|
|
8
|
+
/// @custom:member preventSplitOperatorAdjustingTiers A flag indicating if the revnet's split operator should be
|
|
9
|
+
/// prevented from adding tiers and removing tiers that are allowed to be removed.
|
|
10
|
+
/// @custom:member preventSplitOperatorUpdatingMetadata A flag indicating if the revnet's split operator should be
|
|
11
|
+
/// prevented from updating the 721's metadata.
|
|
12
|
+
/// @custom:member preventSplitOperatorMinting A flag indicating if the revnet's split operator should be prevented from
|
|
13
|
+
/// minting 721's from tiers that allow it.
|
|
14
|
+
/// @custom:member preventSplitOperatorIncreasingDiscountPercent A flag indicating if the revnet's split operator should
|
|
15
|
+
/// be prevented from increasing the discount of a tier.
|
|
18
16
|
struct REVDeploy721TiersHookConfig {
|
|
19
17
|
REVBaseline721HookConfig baseline721HookConfiguration;
|
|
20
18
|
bytes32 salt;
|
|
21
|
-
bool
|
|
22
|
-
bool
|
|
23
|
-
bool
|
|
24
|
-
bool
|
|
19
|
+
bool preventSplitOperatorAdjustingTiers;
|
|
20
|
+
bool preventSplitOperatorUpdatingMetadata;
|
|
21
|
+
bool preventSplitOperatorMinting;
|
|
22
|
+
bool preventSplitOperatorIncreasingDiscountPercent;
|
|
25
23
|
}
|