@rev-net/core-v6 0.0.66 → 0.0.68
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/package.json +1 -1
- package/references/operations.md +36 -17
- package/references/runtime.md +6 -5
- package/script/Deploy.s.sol +6 -3
- package/src/REVDeployer.sol +125 -285
- package/src/REVOwner.sol +325 -24
- package/src/interfaces/IREVDeployer.sol +0 -50
- package/src/interfaces/IREVOwner.sol +117 -6
- package/src/structs/REVOwnerAutoIssuance.sol +14 -0
- package/src/structs/REVOwnerExtraGrant.sol +12 -0
- package/src/structs/REVOwnerRevnetInit.sol +29 -0
package/src/REVDeployer.sol
CHANGED
|
@@ -40,6 +40,9 @@ import {REVAutoIssuance} from "./structs/REVAutoIssuance.sol";
|
|
|
40
40
|
import {REVConfig} from "./structs/REVConfig.sol";
|
|
41
41
|
import {REVCroptopAllowedPost} from "./structs/REVCroptopAllowedPost.sol";
|
|
42
42
|
import {REVDeploy721TiersHookConfig} from "./structs/REVDeploy721TiersHookConfig.sol";
|
|
43
|
+
import {REVOwnerAutoIssuance} from "./structs/REVOwnerAutoIssuance.sol";
|
|
44
|
+
import {REVOwnerExtraGrant} from "./structs/REVOwnerExtraGrant.sol";
|
|
45
|
+
import {REVOwnerRevnetInit} from "./structs/REVOwnerRevnetInit.sol";
|
|
43
46
|
import {REVStageConfig} from "./structs/REVStageConfig.sol";
|
|
44
47
|
import {REVSuckerDeploymentConfig} from "./structs/REVSuckerDeploymentConfig.sol";
|
|
45
48
|
|
|
@@ -60,10 +63,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
60
63
|
error REVDeployer_AutoIssuanceBeneficiaryZeroAddress(uint256 stageIndex, uint256 autoIssuanceIndex);
|
|
61
64
|
error REVDeployer_CashOutsCantBeTurnedOffCompletely(uint256 cashOutTaxRate, uint256 maxCashOutTaxRate);
|
|
62
65
|
error REVDeployer_MustHaveSplits(uint256 stageIndex, uint256 splitPercent);
|
|
63
|
-
error REVDeployer_NothingToAutoIssue(uint256 revnetId, uint256 stageId, address beneficiary);
|
|
64
|
-
error REVDeployer_NothingToBurn(uint256 revnetId, address holder);
|
|
65
66
|
error REVDeployer_RulesetDoesNotAllowDeployingSuckers(uint256 revnetId);
|
|
66
|
-
error REVDeployer_StageNotStarted(uint256 stageId);
|
|
67
67
|
error REVDeployer_StagesRequired(uint256 stageCount);
|
|
68
68
|
error REVDeployer_StageTimesMustIncrease(uint256 stageIndex, uint256 previousStageStart, uint256 effectiveStart);
|
|
69
69
|
error REVDeployer_Unauthorized(uint256 revnetId, address caller);
|
|
@@ -142,31 +142,12 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
142
142
|
// --------------------- public stored properties -------------------- //
|
|
143
143
|
//*********************************************************************//
|
|
144
144
|
|
|
145
|
-
/// @notice The number of revnet tokens which can be "auto-minted" (minted without payments)
|
|
146
|
-
/// for a specific beneficiary during a stage. Think of this as a per-stage premint.
|
|
147
|
-
/// @dev These tokens can be minted with `autoIssueFor(…)`.
|
|
148
|
-
/// @custom:param revnetId The ID of the revnet to check.
|
|
149
|
-
/// @custom:param stageId The ID of the stage to check.
|
|
150
|
-
/// @custom:param beneficiary The beneficiary to check.
|
|
151
|
-
mapping(uint256 revnetId => mapping(uint256 stageId => mapping(address beneficiary => uint256)))
|
|
152
|
-
public
|
|
153
|
-
override amountToAutoIssue;
|
|
154
|
-
|
|
155
145
|
/// @notice The hashed encoded configuration of each revnet.
|
|
156
146
|
/// @dev This is used to ensure that the encoded configuration of a revnet is the same when deploying suckers for
|
|
157
147
|
/// omnichain operations.
|
|
158
148
|
/// @custom:param revnetId The ID of the revnet to look up.
|
|
159
149
|
mapping(uint256 revnetId => bytes32 hashedEncodedConfiguration) public override hashedEncodedConfigurationOf;
|
|
160
150
|
|
|
161
|
-
//*********************************************************************//
|
|
162
|
-
// ------------------- internal stored properties -------------------- //
|
|
163
|
-
//*********************************************************************//
|
|
164
|
-
|
|
165
|
-
/// @notice A list of `JBPermissonIds` indices to grant to the operator of a specific revnet.
|
|
166
|
-
/// @dev These should be set in the revnet's deployment process.
|
|
167
|
-
/// @custom:param revnetId The ID of the revnet to look up.
|
|
168
|
-
mapping(uint256 revnetId => uint256[]) internal _extraOperatorPermissions;
|
|
169
|
-
|
|
170
151
|
//*********************************************************************//
|
|
171
152
|
// -------------------------- constructor ---------------------------- //
|
|
172
153
|
//*********************************************************************//
|
|
@@ -213,20 +194,25 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
213
194
|
LOANS = loans;
|
|
214
195
|
OWNER = owner;
|
|
215
196
|
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
197
|
+
// Wildcard-grant `SET_BUYBACK_POOL` to the buyback hook on this contract's account so the hook can
|
|
198
|
+
// initialize and configure pools for every revnet during the setup window where this contract still
|
|
199
|
+
// holds the JBProjects NFT, before ownership is handed to REVOwner at the end of `_deployRevnetFor`.
|
|
200
|
+
uint8[] memory buybackPermissionIds = new uint8[](1);
|
|
201
|
+
buybackPermissionIds[0] = JBPermissionIds.SET_BUYBACK_POOL;
|
|
202
|
+
PERMISSIONS.setPermissionsFor({
|
|
203
|
+
account: address(this),
|
|
204
|
+
permissionsData: JBPermissionsData({
|
|
205
|
+
operator: address(buybackHook), projectId: 0, permissionIds: buybackPermissionIds
|
|
206
|
+
})
|
|
207
|
+
});
|
|
223
208
|
}
|
|
224
209
|
|
|
225
210
|
//*********************************************************************//
|
|
226
211
|
// ------------------------- external views -------------------------- //
|
|
227
212
|
//*********************************************************************//
|
|
228
213
|
|
|
229
|
-
/// @dev
|
|
214
|
+
/// @dev Required to receive the JBProjects NFT briefly while initializing an existing project as a revnet.
|
|
215
|
+
/// The NFT is then forwarded to `OWNER` at the end of `_deployRevnetFor`.
|
|
230
216
|
function onERC721Received(address, address, uint256, bytes calldata) external view returns (bytes4) {
|
|
231
217
|
// Make sure the 721 received is from the `JBProjects` contract.
|
|
232
218
|
if (msg.sender != address(PROJECTS)) revert();
|
|
@@ -238,21 +224,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
238
224
|
// -------------------------- public views --------------------------- //
|
|
239
225
|
//*********************************************************************//
|
|
240
226
|
|
|
241
|
-
/// @notice Check whether an address is a revnet's operator.
|
|
242
|
-
/// @param revnetId The ID of the revnet to check.
|
|
243
|
-
/// @param addr The address to check.
|
|
244
|
-
/// @return flag A flag indicating whether the address is the revnet's operator.
|
|
245
|
-
function isOperatorOf(uint256 revnetId, address addr) public view override returns (bool) {
|
|
246
|
-
return PERMISSIONS.hasPermissions({
|
|
247
|
-
operator: addr,
|
|
248
|
-
account: address(this),
|
|
249
|
-
projectId: revnetId,
|
|
250
|
-
permissionIds: _operatorPermissionIndexesOf(revnetId),
|
|
251
|
-
includeRoot: false,
|
|
252
|
-
includeWildcardProjectId: false
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
227
|
/// @notice Indicates if this contract adheres to the specified interface.
|
|
257
228
|
/// @dev See `IERC165.supportsInterface`.
|
|
258
229
|
/// @return A flag indicating if the provided interface ID is supported.
|
|
@@ -264,11 +235,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
264
235
|
// -------------------------- internal views ------------------------- //
|
|
265
236
|
//*********************************************************************//
|
|
266
237
|
|
|
267
|
-
/// @notice If the specified address is not the revnet's current operator, revert.
|
|
238
|
+
/// @notice If the specified address is not the revnet's current operator on the REVOwner registry, revert.
|
|
268
239
|
/// @param revnetId The ID of the revnet to check.
|
|
269
240
|
/// @param operator The address to check.
|
|
270
241
|
function _checkIfIsOperatorOf(uint256 revnetId, address operator) internal view {
|
|
271
|
-
if (!isOperatorOf({revnetId: revnetId, addr: operator})) {
|
|
242
|
+
if (!REVOwner(OWNER).isOperatorOf({revnetId: revnetId, addr: operator})) {
|
|
272
243
|
revert REVDeployer_Unauthorized({revnetId: revnetId, caller: operator});
|
|
273
244
|
}
|
|
274
245
|
}
|
|
@@ -374,39 +345,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
374
345
|
}
|
|
375
346
|
}
|
|
376
347
|
|
|
377
|
-
/// @notice Returns the permissions that the operator should have for a revnet.
|
|
378
|
-
/// @param revnetId The ID of the revnet to look up.
|
|
379
|
-
/// @return allOperatorPermissions The permissions the operator should have for the revnet,
|
|
380
|
-
/// including both default and custom permissions.
|
|
381
|
-
function _operatorPermissionIndexesOf(uint256 revnetId)
|
|
382
|
-
internal
|
|
383
|
-
view
|
|
384
|
-
returns (uint256[] memory allOperatorPermissions)
|
|
385
|
-
{
|
|
386
|
-
// Keep a reference to the custom operator permissions.
|
|
387
|
-
uint256[] memory customOperatorPermissionIndexes = _extraOperatorPermissions[revnetId];
|
|
388
|
-
|
|
389
|
-
// Make the array that merges the default and custom operator permissions.
|
|
390
|
-
allOperatorPermissions = new uint256[](9 + customOperatorPermissionIndexes.length);
|
|
391
|
-
allOperatorPermissions[0] = JBPermissionIds.SET_SPLIT_GROUPS;
|
|
392
|
-
allOperatorPermissions[1] = JBPermissionIds.SET_BUYBACK_POOL;
|
|
393
|
-
allOperatorPermissions[2] = JBPermissionIds.SET_BUYBACK_TWAP;
|
|
394
|
-
allOperatorPermissions[3] = JBPermissionIds.SET_PROJECT_URI;
|
|
395
|
-
allOperatorPermissions[4] = JBPermissionIds.SUCKER_SAFETY;
|
|
396
|
-
allOperatorPermissions[5] = JBPermissionIds.SET_BUYBACK_HOOK;
|
|
397
|
-
allOperatorPermissions[6] = JBPermissionIds.SET_ROUTER_TERMINAL;
|
|
398
|
-
allOperatorPermissions[7] = JBPermissionIds.SET_TOKEN_METADATA;
|
|
399
|
-
allOperatorPermissions[8] = JBPermissionIds.SIGN_FOR_ERC20;
|
|
400
|
-
|
|
401
|
-
// Copy the custom permissions into the array.
|
|
402
|
-
for (uint256 i; i < customOperatorPermissionIndexes.length;) {
|
|
403
|
-
allOperatorPermissions[9 + i] = customOperatorPermissionIndexes[i];
|
|
404
|
-
unchecked {
|
|
405
|
-
++i;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
348
|
/// @notice Try to initialize a Uniswap V4 buyback pool for a terminal token at its fair issuance price.
|
|
411
349
|
/// @dev Called after the ERC-20 token is deployed so the pool can be initialized in the PoolManager.
|
|
412
350
|
/// Computes `sqrtPriceX96` from `initialIssuance` so the pool starts at the same price as the bonding curve.
|
|
@@ -471,57 +409,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
471
409
|
// --------------------- external transactions ----------------------- //
|
|
472
410
|
//*********************************************************************//
|
|
473
411
|
|
|
474
|
-
/// @notice Auto-mint a revnet's tokens from a stage for a beneficiary.
|
|
475
|
-
/// @param revnetId The ID of the revnet to auto-mint tokens for.
|
|
476
|
-
/// @param stageId The ID of the stage to auto-mint tokens from.
|
|
477
|
-
/// @param beneficiary The address to send auto-minted tokens to.
|
|
478
|
-
function autoIssueFor(uint256 revnetId, uint256 stageId, address beneficiary) external override {
|
|
479
|
-
// Get the ruleset for the stage to check if it has started.
|
|
480
|
-
// Stage IDs are `block.timestamp + i` where `i` is the stage index. These match real JB ruleset IDs
|
|
481
|
-
// because JBRulesets assigns IDs the same way: `latestId >= block.timestamp ? latestId + 1 : block.timestamp`
|
|
482
|
-
// (see JBRulesets.sol L172). When all stages are queued in a single deployFor() call, the sequential
|
|
483
|
-
// IDs `block.timestamp`, `block.timestamp + 1`, ... exactly correspond to the JB-assigned ruleset IDs.
|
|
484
|
-
// The returned `ruleset.start` contains the derived start time (from `deriveStartFrom` using the stage's
|
|
485
|
-
// `mustStartAtOrAfter`), NOT the queue timestamp — so the timing guard correctly blocks early claims.
|
|
486
|
-
(JBRuleset memory ruleset,) = CONTROLLER.getRulesetOf({projectId: revnetId, rulesetId: stageId});
|
|
487
|
-
|
|
488
|
-
// Make sure the stage has started.
|
|
489
|
-
// forge-lint: disable-next-line(block-timestamp)
|
|
490
|
-
if (ruleset.start > block.timestamp) {
|
|
491
|
-
revert REVDeployer_StageNotStarted({stageId: stageId});
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// Get a reference to the number of tokens to auto-issue.
|
|
495
|
-
uint256 count = amountToAutoIssue[revnetId][stageId][beneficiary];
|
|
496
|
-
|
|
497
|
-
// If there's nothing to auto-mint, return.
|
|
498
|
-
if (count == 0) {
|
|
499
|
-
revert REVDeployer_NothingToAutoIssue({revnetId: revnetId, stageId: stageId, beneficiary: beneficiary});
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Reset the auto-mint amount.
|
|
503
|
-
amountToAutoIssue[revnetId][stageId][beneficiary] = 0;
|
|
504
|
-
|
|
505
|
-
emit AutoIssue({
|
|
506
|
-
revnetId: revnetId, stageId: stageId, beneficiary: beneficiary, count: count, caller: _msgSender()
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
// Mint the tokens.
|
|
510
|
-
CONTROLLER.mintTokensOf({
|
|
511
|
-
projectId: revnetId, tokenCount: count, beneficiary: beneficiary, memo: "", useReservedPercent: false
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
/// @notice Burn any of a revnet's tokens held by this contract.
|
|
516
|
-
/// @dev Project tokens can end up here from reserved token distribution when splits don't sum to 100%.
|
|
517
|
-
/// @param revnetId The ID of the revnet to burn tokens for.
|
|
518
|
-
function burnHeldTokensOf(uint256 revnetId) external override {
|
|
519
|
-
uint256 balance = CONTROLLER.TOKENS().totalBalanceOf({holder: address(this), projectId: revnetId});
|
|
520
|
-
if (balance == 0) revert REVDeployer_NothingToBurn({revnetId: revnetId, holder: address(this)});
|
|
521
|
-
CONTROLLER.burnTokensOf({holder: address(this), projectId: revnetId, tokenCount: balance, memo: ""});
|
|
522
|
-
emit BurnHeldTokens({revnetId: revnetId, count: balance, caller: _msgSender()});
|
|
523
|
-
}
|
|
524
|
-
|
|
525
412
|
/// @notice Launch a revnet, or initialize an existing Juicebox project as a revnet.
|
|
526
413
|
/// @dev When initializing an existing project (revnetId != 0):
|
|
527
414
|
/// - The project must not yet have a controller or rulesets. `JBController.launchRulesetsFor` enforces this —
|
|
@@ -588,7 +475,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
588
475
|
if (shouldDeployNewRevnet) revnetId = PROJECTS.createFor(address(this));
|
|
589
476
|
|
|
590
477
|
// Deploy the revnet (project, rulesets, ERC-20, suckers, etc.).
|
|
591
|
-
bytes32 encodedConfigurationHash = _deployRevnetFor({
|
|
478
|
+
(bytes32 encodedConfigurationHash, REVOwnerRevnetInit memory ownerInit) = _deployRevnetFor({
|
|
592
479
|
revnetId: revnetId,
|
|
593
480
|
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
594
481
|
configuration: configuration,
|
|
@@ -609,19 +496,18 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
609
496
|
});
|
|
610
497
|
}
|
|
611
498
|
|
|
612
|
-
// Store the tiered ERC-721 hook in the owner contract.
|
|
613
|
-
REVOwner(OWNER).setTiered721HookOf({revnetId: revnetId, hook: hook});
|
|
614
|
-
|
|
615
499
|
// Grant the operator all 721 permissions (no prevent* flags for default config).
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
500
|
+
ownerInit.extraOperatorPermissionIds = new uint256[](4);
|
|
501
|
+
ownerInit.extraOperatorPermissionIds[0] = JBPermissionIds.ADJUST_721_TIERS;
|
|
502
|
+
ownerInit.extraOperatorPermissionIds[1] = JBPermissionIds.SET_721_METADATA;
|
|
503
|
+
ownerInit.extraOperatorPermissionIds[2] = JBPermissionIds.MINT_721;
|
|
504
|
+
ownerInit.extraOperatorPermissionIds[3] = JBPermissionIds.SET_721_DISCOUNT_PERCENT;
|
|
505
|
+
|
|
506
|
+
ownerInit.tiered721Hook = hook;
|
|
507
|
+
ownerInit.operator = configuration.operator;
|
|
622
508
|
|
|
623
|
-
//
|
|
624
|
-
|
|
509
|
+
// Bind every piece of revnet-scoped state on the owner contract in a single call.
|
|
510
|
+
REVOwner(OWNER).initializeRevnet({revnetId: revnetId, init: ownerInit});
|
|
625
511
|
|
|
626
512
|
return (revnetId, hook);
|
|
627
513
|
}
|
|
@@ -659,27 +545,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
659
545
|
});
|
|
660
546
|
}
|
|
661
547
|
|
|
662
|
-
/// @notice Change a revnet's operator.
|
|
663
|
-
/// @dev Only a revnet's current operator can set a new operator.
|
|
664
|
-
/// @dev Passing `address(0)` as `newOperator` relinquishes operator powers permanently — the permissions
|
|
665
|
-
/// are granted to the zero address (which cannot execute transactions), effectively burning them.
|
|
666
|
-
/// @param revnetId The ID of the revnet to change the operator for.
|
|
667
|
-
/// @param newOperator The new operator's address. Use `address(0)` to relinquish operator powers.
|
|
668
|
-
function setOperatorOf(uint256 revnetId, address newOperator) external override {
|
|
669
|
-
// Enforce permissions.
|
|
670
|
-
_checkIfIsOperatorOf({revnetId: revnetId, operator: _msgSender()});
|
|
671
|
-
|
|
672
|
-
emit ReplaceOperator({revnetId: revnetId, newOperator: newOperator, caller: _msgSender()});
|
|
673
|
-
|
|
674
|
-
// Remove operator permissions from the old operator.
|
|
675
|
-
_setPermissionsFor({
|
|
676
|
-
account: address(this), operator: _msgSender(), revnetId: revnetId, permissionIds: new uint8[](0)
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// Set the new operator.
|
|
680
|
-
_setOperatorOf({revnetId: revnetId, operator: newOperator});
|
|
681
|
-
}
|
|
682
|
-
|
|
683
548
|
//*********************************************************************//
|
|
684
549
|
// --------------------- internal transactions ----------------------- //
|
|
685
550
|
//*********************************************************************//
|
|
@@ -699,7 +564,7 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
699
564
|
returns (IJB721TiersHook hook)
|
|
700
565
|
{
|
|
701
566
|
// Deploy the revnet (project, rulesets, ERC-20, suckers, etc.).
|
|
702
|
-
bytes32 encodedConfigurationHash = _deployRevnetFor({
|
|
567
|
+
(bytes32 encodedConfigurationHash, REVOwnerRevnetInit memory ownerInit) = _deployRevnetFor({
|
|
703
568
|
revnetId: revnetId,
|
|
704
569
|
shouldDeployNewRevnet: shouldDeployNewRevnet,
|
|
705
570
|
configuration: configuration,
|
|
@@ -733,35 +598,33 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
733
598
|
salt: keccak256(abi.encode(tiered721HookConfiguration.salt, encodedConfigurationHash, _msgSender()))
|
|
734
599
|
});
|
|
735
600
|
|
|
736
|
-
//
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
if (!tiered721HookConfiguration.preventOperatorIncreasingDiscountPercent) {
|
|
760
|
-
_extraOperatorPermissions[revnetId].push(JBPermissionIds.SET_721_DISCOUNT_PERCENT);
|
|
601
|
+
// Build the 721 permission additions based on the deployer's `preventOperator*` flags.
|
|
602
|
+
{
|
|
603
|
+
uint256 extraCount;
|
|
604
|
+
if (!tiered721HookConfiguration.preventOperatorAdjustingTiers) ++extraCount;
|
|
605
|
+
if (!tiered721HookConfiguration.preventOperatorUpdatingMetadata) ++extraCount;
|
|
606
|
+
if (!tiered721HookConfiguration.preventOperatorMinting) ++extraCount;
|
|
607
|
+
if (!tiered721HookConfiguration.preventOperatorIncreasingDiscountPercent) ++extraCount;
|
|
608
|
+
|
|
609
|
+
uint256[] memory extraPermissions = new uint256[](extraCount);
|
|
610
|
+
uint256 idx;
|
|
611
|
+
if (!tiered721HookConfiguration.preventOperatorAdjustingTiers) {
|
|
612
|
+
extraPermissions[idx++] = JBPermissionIds.ADJUST_721_TIERS;
|
|
613
|
+
}
|
|
614
|
+
if (!tiered721HookConfiguration.preventOperatorUpdatingMetadata) {
|
|
615
|
+
extraPermissions[idx++] = JBPermissionIds.SET_721_METADATA;
|
|
616
|
+
}
|
|
617
|
+
if (!tiered721HookConfiguration.preventOperatorMinting) {
|
|
618
|
+
extraPermissions[idx++] = JBPermissionIds.MINT_721;
|
|
619
|
+
}
|
|
620
|
+
if (!tiered721HookConfiguration.preventOperatorIncreasingDiscountPercent) {
|
|
621
|
+
extraPermissions[idx++] = JBPermissionIds.SET_721_DISCOUNT_PERCENT;
|
|
622
|
+
}
|
|
623
|
+
ownerInit.extraOperatorPermissionIds = extraPermissions;
|
|
761
624
|
}
|
|
762
625
|
|
|
763
|
-
|
|
764
|
-
|
|
626
|
+
ownerInit.tiered721Hook = hook;
|
|
627
|
+
ownerInit.operator = configuration.operator;
|
|
765
628
|
|
|
766
629
|
// If there are posts to allow, configure them.
|
|
767
630
|
if (allowedPosts.length != 0) {
|
|
@@ -791,11 +654,14 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
791
654
|
// Set up the allowed posts in the publisher.
|
|
792
655
|
PUBLISHER.configurePostingCriteriaFor({allowedPosts: formattedAllowedPosts});
|
|
793
656
|
|
|
794
|
-
// Give the croptop publisher permission to post new ERC-721 tiers on
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
657
|
+
// Give the croptop publisher permission to post new ERC-721 tiers on the revnet's behalf.
|
|
658
|
+
ownerInit.extraGrants = new REVOwnerExtraGrant[](1);
|
|
659
|
+
ownerInit.extraGrants[0] =
|
|
660
|
+
REVOwnerExtraGrant({operator: address(PUBLISHER), permissionId: JBPermissionIds.ADJUST_721_TIERS});
|
|
798
661
|
}
|
|
662
|
+
|
|
663
|
+
// Bind every piece of revnet-scoped state on the owner contract in a single call.
|
|
664
|
+
REVOwner(OWNER).initializeRevnet({revnetId: revnetId, init: ownerInit});
|
|
799
665
|
}
|
|
800
666
|
|
|
801
667
|
/// @notice Deploy a revnet, or initialize an existing Juicebox project as a revnet.
|
|
@@ -820,11 +686,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
820
686
|
REVSuckerDeploymentConfig calldata suckerDeploymentConfiguration
|
|
821
687
|
)
|
|
822
688
|
internal
|
|
823
|
-
returns (bytes32 encodedConfigurationHash)
|
|
689
|
+
returns (bytes32 encodedConfigurationHash, REVOwnerRevnetInit memory ownerInit)
|
|
824
690
|
{
|
|
825
691
|
// Normalize and encode the configurations.
|
|
826
692
|
JBRulesetConfig[] memory rulesetConfigurations;
|
|
827
|
-
(rulesetConfigurations, encodedConfigurationHash) = _makeRulesetConfigurations({
|
|
693
|
+
(rulesetConfigurations, encodedConfigurationHash, ownerInit.autoIssuances) = _makeRulesetConfigurations({
|
|
828
694
|
revnetId: revnetId, configuration: configuration, accountingContextsToAccept: accountingContextsToAccept
|
|
829
695
|
});
|
|
830
696
|
|
|
@@ -832,20 +698,17 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
832
698
|
JBTerminalConfig[] memory terminalConfigurations =
|
|
833
699
|
_makeTerminalConfigurations({accountingContextsToAccept: accountingContextsToAccept});
|
|
834
700
|
|
|
835
|
-
|
|
701
|
+
// Store the hash before setup callbacks so reentrant readers cannot observe a zero configuration hash. Any
|
|
702
|
+
// subsequent revert rolls this write back.
|
|
703
|
+
hashedEncodedConfigurationOf[revnetId] = encodedConfigurationHash;
|
|
704
|
+
|
|
836
705
|
if (!shouldDeployNewRevnet) {
|
|
837
706
|
// Keep a reference to the Juicebox project's owner.
|
|
838
|
-
owner = PROJECTS.ownerOf(revnetId);
|
|
707
|
+
address owner = PROJECTS.ownerOf(revnetId);
|
|
839
708
|
|
|
840
709
|
// Make sure the caller is the owner of the Juicebox project.
|
|
841
710
|
if (_msgSender() != owner) revert REVDeployer_Unauthorized(revnetId, _msgSender());
|
|
842
|
-
}
|
|
843
711
|
|
|
844
|
-
// Store the hash before setup callbacks so reentrant readers cannot observe a zero configuration hash. Any
|
|
845
|
-
// subsequent revert rolls this write back.
|
|
846
|
-
hashedEncodedConfigurationOf[revnetId] = encodedConfigurationHash;
|
|
847
|
-
|
|
848
|
-
if (!shouldDeployNewRevnet) {
|
|
849
712
|
// Initialize the existing Juicebox project as a revnet by transferring the `JBProjects` NFT to this
|
|
850
713
|
// deployer. This is irreversible.
|
|
851
714
|
IERC721(PROJECTS).safeTransferFrom({from: owner, to: address(this), tokenId: revnetId});
|
|
@@ -860,10 +723,10 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
860
723
|
memo: ""
|
|
861
724
|
});
|
|
862
725
|
|
|
863
|
-
//
|
|
864
|
-
//
|
|
865
|
-
|
|
866
|
-
|
|
726
|
+
// Compute the cash out delay if the revnet's stages are already in progress. This prevents cash out
|
|
727
|
+
// liquidity/arbitrage issues for existing revnets which are deploying to a new chain.
|
|
728
|
+
ownerInit.cashOutDelay =
|
|
729
|
+
_computeCashOutDelayIfNeeded({revnetId: revnetId, firstStageConfig: configuration.stageConfigurations[0]});
|
|
867
730
|
|
|
868
731
|
// Deploy the revnet's ERC-20 token.
|
|
869
732
|
CONTROLLER.deployERC20For({
|
|
@@ -886,6 +749,9 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
886
749
|
}
|
|
887
750
|
}
|
|
888
751
|
|
|
752
|
+
// Transfer the JBProjects NFT to REVOwner. REVOwner is the project's authoritative owner.
|
|
753
|
+
IERC721(PROJECTS).safeTransferFrom({from: address(this), to: OWNER, tokenId: revnetId});
|
|
754
|
+
|
|
889
755
|
// Deploy the suckers (if applicable).
|
|
890
756
|
if (suckerDeploymentConfiguration.salt != bytes32(0)) {
|
|
891
757
|
_deploySuckersFor({
|
|
@@ -906,6 +772,8 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
906
772
|
});
|
|
907
773
|
}
|
|
908
774
|
|
|
775
|
+
/// @notice Deploy suckers for a revnet against the sucker registry, scoped on REVOwner's account.
|
|
776
|
+
/// @param revnetId The ID of the revnet to deploy suckers for.
|
|
909
777
|
/// @param encodedConfigurationHash A hash that represents the revnet's configuration.
|
|
910
778
|
/// @param suckerDeploymentConfiguration The suckers to set up for the revnet.
|
|
911
779
|
function _deploySuckersFor(
|
|
@@ -923,8 +791,6 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
923
791
|
caller: _msgSender()
|
|
924
792
|
});
|
|
925
793
|
|
|
926
|
-
// Include the caller so two revnets with identical configuration and user salt cannot collide. Same-address
|
|
927
|
-
// cross-chain deployment still works when the same operator calls this helper on each chain.
|
|
928
794
|
suckers = SUCKER_REGISTRY.deploySuckersFor({
|
|
929
795
|
projectId: revnetId,
|
|
930
796
|
salt: keccak256(abi.encode(encodedConfigurationHash, suckerDeploymentConfiguration.salt, _msgSender())),
|
|
@@ -954,7 +820,11 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
954
820
|
JBAccountingContext[] calldata accountingContextsToAccept
|
|
955
821
|
)
|
|
956
822
|
internal
|
|
957
|
-
returns (
|
|
823
|
+
returns (
|
|
824
|
+
JBRulesetConfig[] memory rulesetConfigurations,
|
|
825
|
+
bytes32 encodedConfigurationHash,
|
|
826
|
+
REVOwnerAutoIssuance[] memory autoIssuances
|
|
827
|
+
)
|
|
958
828
|
{
|
|
959
829
|
// If there are no stages, revert.
|
|
960
830
|
if (configuration.stageConfigurations.length == 0) {
|
|
@@ -977,6 +847,25 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
977
847
|
JBFundAccessLimitGroup[] memory fundAccessLimitGroups =
|
|
978
848
|
_makeLoanFundAccessLimits({accountingContextsToAccept: accountingContextsToAccept});
|
|
979
849
|
|
|
850
|
+
// Pre-count auto-issuances that will land on this chain so the owner-init array can be sized exactly.
|
|
851
|
+
uint256 autoIssuanceCount;
|
|
852
|
+
for (uint256 i; i < configuration.stageConfigurations.length;) {
|
|
853
|
+
REVAutoIssuance[] calldata stageAutoIssuances = configuration.stageConfigurations[i].autoIssuances;
|
|
854
|
+
for (uint256 j; j < stageAutoIssuances.length;) {
|
|
855
|
+
if (stageAutoIssuances[j].count != 0 && stageAutoIssuances[j].chainId == block.chainid) {
|
|
856
|
+
++autoIssuanceCount;
|
|
857
|
+
}
|
|
858
|
+
unchecked {
|
|
859
|
+
++j;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
unchecked {
|
|
863
|
+
++i;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
autoIssuances = new REVOwnerAutoIssuance[](autoIssuanceCount);
|
|
867
|
+
uint256 autoIssuanceIdx;
|
|
868
|
+
|
|
980
869
|
// Track the previous stage's effective start time for ordering validation.
|
|
981
870
|
// When stage 0 uses `startsAtOrAfter == 0`, the effective value is `block.timestamp`.
|
|
982
871
|
// Subsequent stages must be validated against this normalized value, not the raw calldata,
|
|
@@ -1069,13 +958,16 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
1069
958
|
caller: _msgSender()
|
|
1070
959
|
});
|
|
1071
960
|
|
|
1072
|
-
//
|
|
1073
|
-
//
|
|
1074
|
-
//
|
|
961
|
+
// Record the amount of tokens that can be auto-minted on this chain during this stage. The stage
|
|
962
|
+
// ID is `block.timestamp + i`. This matches the ruleset ID that JBRulesets assigns because
|
|
963
|
+
// JBRulesets uses `latestId >= block.timestamp ? latestId + 1 : block.timestamp`
|
|
1075
964
|
// (JBRulesets.sol L172), producing the same sequential IDs when all stages are queued in one tx.
|
|
1076
|
-
// `autoIssueFor` later calls `getRulesetOf(revnetId, stageId)` — the returned
|
|
1077
|
-
// is the derived start time (not the queue time), so the timing guard works
|
|
1078
|
-
|
|
965
|
+
// `REVOwner.autoIssueFor` later calls `getRulesetOf(revnetId, stageId)` — the returned
|
|
966
|
+
// `ruleset.start` is the derived start time (not the queue time), so the timing guard works
|
|
967
|
+
// correctly.
|
|
968
|
+
autoIssuances[autoIssuanceIdx++] = REVOwnerAutoIssuance({
|
|
969
|
+
stageId: block.timestamp + i, beneficiary: autoIssuance.beneficiary, count: autoIssuance.count
|
|
970
|
+
});
|
|
1079
971
|
}
|
|
1080
972
|
unchecked {
|
|
1081
973
|
++i;
|
|
@@ -1086,79 +978,27 @@ contract REVDeployer is ERC2771Context, IREVDeployer, IERC721Receiver {
|
|
|
1086
978
|
encodedConfigurationHash = keccak256(encodedConfiguration);
|
|
1087
979
|
}
|
|
1088
980
|
|
|
1089
|
-
/// @notice
|
|
1090
|
-
/// @dev
|
|
1091
|
-
///
|
|
981
|
+
/// @notice Compute the cash out delay for the revnet's first stage if its stages are already in progress.
|
|
982
|
+
/// @dev Returns 0 when no delay is needed. Emits `SetCashOutDelay` so deployer-side telemetry stays unchanged
|
|
983
|
+
/// even though the actual storage write happens later inside `REVOwner.initializeRevnet`.
|
|
984
|
+
/// @param revnetId The ID of the revnet to compute the cash out delay for.
|
|
1092
985
|
/// @param firstStageConfig The revnet's first stage.
|
|
1093
|
-
|
|
986
|
+
/// @return cashOutDelay The timestamp after which cash outs are allowed, or 0 if no delay applies.
|
|
987
|
+
function _computeCashOutDelayIfNeeded(
|
|
988
|
+
uint256 revnetId,
|
|
989
|
+
REVStageConfig calldata firstStageConfig
|
|
990
|
+
)
|
|
991
|
+
internal
|
|
992
|
+
returns (uint256 cashOutDelay)
|
|
993
|
+
{
|
|
1094
994
|
// If this is the first revnet being deployed (with a `startsAtOrAfter` of 0),
|
|
1095
995
|
// or if the first stage hasn't started yet, we don't need to set a cash out delay.
|
|
1096
996
|
// forge-lint: disable-next-line(block-timestamp)
|
|
1097
|
-
if (firstStageConfig.startsAtOrAfter == 0 || firstStageConfig.startsAtOrAfter >= block.timestamp) return;
|
|
997
|
+
if (firstStageConfig.startsAtOrAfter == 0 || firstStageConfig.startsAtOrAfter >= block.timestamp) return 0;
|
|
1098
998
|
|
|
1099
999
|
// Calculate the timestamp at which the cash out delay ends.
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
// Store the cash out delay in the owner contract.
|
|
1103
|
-
REVOwner(OWNER).setCashOutDelayOf({revnetId: revnetId, cashOutDelay: cashOutDelay});
|
|
1000
|
+
cashOutDelay = block.timestamp + CASH_OUT_DELAY;
|
|
1104
1001
|
|
|
1105
1002
|
emit SetCashOutDelay({revnetId: revnetId, cashOutDelay: cashOutDelay, caller: _msgSender()});
|
|
1106
1003
|
}
|
|
1107
|
-
|
|
1108
|
-
/// @notice Grant a permission to an address (an "operator").
|
|
1109
|
-
/// @param operator The address to grant the permission to.
|
|
1110
|
-
/// @param revnetId The ID of the revnet to scope the permission for.
|
|
1111
|
-
/// @param permissionId The ID of the permission to grant. See `JBPermissionIds`.
|
|
1112
|
-
function _setPermission(address operator, uint256 revnetId, uint8 permissionId) internal {
|
|
1113
|
-
uint8[] memory permissionsIds = new uint8[](1);
|
|
1114
|
-
permissionsIds[0] = permissionId;
|
|
1115
|
-
|
|
1116
|
-
// Give the operator the permission.
|
|
1117
|
-
_setPermissionsFor({
|
|
1118
|
-
account: address(this), operator: operator, revnetId: revnetId, permissionIds: permissionsIds
|
|
1119
|
-
});
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
/// @notice Grant permissions to an address (an "operator").
|
|
1123
|
-
/// @param account The account granting the permissions.
|
|
1124
|
-
/// @param operator The address to grant the permissions to.
|
|
1125
|
-
/// @param revnetId The ID of the revnet to scope the permissions for.
|
|
1126
|
-
/// @param permissionIds An array of permission IDs to grant. See `JBPermissionIds`.
|
|
1127
|
-
function _setPermissionsFor(
|
|
1128
|
-
address account,
|
|
1129
|
-
address operator,
|
|
1130
|
-
uint256 revnetId,
|
|
1131
|
-
uint8[] memory permissionIds
|
|
1132
|
-
)
|
|
1133
|
-
internal
|
|
1134
|
-
{
|
|
1135
|
-
// Set up the permission data.
|
|
1136
|
-
JBPermissionsData memory permissionData =
|
|
1137
|
-
// forge-lint: disable-next-line(unsafe-typecast)
|
|
1138
|
-
JBPermissionsData({operator: operator, projectId: uint64(revnetId), permissionIds: permissionIds});
|
|
1139
|
-
|
|
1140
|
-
// Set the permissions.
|
|
1141
|
-
PERMISSIONS.setPermissionsFor({account: account, permissionsData: permissionData});
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
/// @notice Give a operator their permissions.
|
|
1145
|
-
/// @dev Only a revnet's current operator can set a new operator, by calling `setOperatorOf(…)`.
|
|
1146
|
-
/// @param revnetId The ID of the revnet to grant operator permissions for.
|
|
1147
|
-
/// @param operator The new operator's address.
|
|
1148
|
-
function _setOperatorOf(uint256 revnetId, address operator) internal {
|
|
1149
|
-
// Get the permission indexes for the operator.
|
|
1150
|
-
uint256[] memory permissionIndexes = _operatorPermissionIndexesOf(revnetId);
|
|
1151
|
-
uint8[] memory permissionIds = new uint8[](permissionIndexes.length);
|
|
1152
|
-
|
|
1153
|
-
for (uint256 i; i < permissionIndexes.length;) {
|
|
1154
|
-
permissionIds[i] = uint8(permissionIndexes[i]);
|
|
1155
|
-
unchecked {
|
|
1156
|
-
++i;
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
_setPermissionsFor({
|
|
1161
|
-
account: address(this), operator: operator, revnetId: revnetId, permissionIds: permissionIds
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
1004
|
}
|