-
Notifications
You must be signed in to change notification settings - Fork 10
/
JBController3_1.sol
878 lines (736 loc) · 33.2 KB
/
JBController3_1.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import '@openzeppelin/contracts/utils/introspection/ERC165.sol';
import '@openzeppelin/contracts/utils/math/SafeCast.sol';
import '@paulrberg/contracts/math/PRBMath.sol';
import './abstract/JBOperatable.sol';
import './interfaces/IJBController3_1.sol';
import './interfaces/IJBMigratable.sol';
import './interfaces/IJBOperatorStore.sol';
import './interfaces/IJBPaymentTerminal.sol';
import './interfaces/IJBProjects.sol';
import './libraries/JBConstants.sol';
import './libraries/JBFundingCycleMetadataResolver.sol';
import './libraries/JBOperations.sol';
import './libraries/JBSplitsGroups.sol';
/**
@notice
Stitches together funding cycles and project tokens, making sure all activity is accounted for and correct.
@dev
Adheres to -
IJBController3_1: General interface for the generic controller methods in this contract that interacts with funding cycles and tokens according to the protocol's rules.
IJBMigratable: Allows migrating to this contract, with a hook called to prepare for the migration.
@dev
Inherits from -
JBOperatable: Several functions in this contract can only be accessed by a project owner, or an address that has been preconfifigured to be an operator of the project.
ERC165: Introspection on interface adherance.
@dev
This Controller has the same functionality as JBController3_1, except it is not backwards compatible with the original IJBController view methods.
*/
contract JBController3_1 is JBOperatable, ERC165, IJBController3_1, IJBMigratable {
// A library that parses the packed funding cycle metadata into a more friendly format.
using JBFundingCycleMetadataResolver for JBFundingCycle;
//*********************************************************************//
// --------------------------- custom errors ------------------------- //
//*********************************************************************//
error BURN_PAUSED_AND_SENDER_NOT_VALID_TERMINAL_DELEGATE();
error CANT_MIGRATE_TO_CURRENT_CONTROLLER();
error FUNDING_CYCLE_ALREADY_LAUNCHED();
error INVALID_BALLOT_REDEMPTION_RATE();
error INVALID_REDEMPTION_RATE();
error INVALID_RESERVED_RATE();
error MIGRATION_NOT_ALLOWED();
error MINT_NOT_ALLOWED_AND_NOT_TERMINAL_DELEGATE();
error NO_BURNABLE_TOKENS();
error NOT_CURRENT_CONTROLLER();
error OVERFLOW_ALERT();
error ZERO_TOKENS_TO_MINT();
//*********************************************************************//
// --------------------- internal stored properties ------------------ //
//*********************************************************************//
/**
@notice
Data regarding the distribution limit of a project during a configuration.
@dev
bits 0-231: The amount of token that a project can distribute per funding cycle.
@dev
bits 232-255: The currency of amount that a project can distribute.
_projectId The ID of the project to get the packed distribution limit data of.
_configuration The configuration during which the packed distribution limit data applies.
_terminal The terminal from which distributions are being limited.
_token The token for which distributions are being limited.
*/
mapping(uint256 => mapping(uint256 => mapping(IJBPaymentTerminal => mapping(address => uint256))))
internal _packedDistributionLimitDataOf;
/**
@notice
Data regarding the overflow allowance of a project during a configuration.
@dev
bits 0-231: The amount of overflow that a project is allowed to tap into on-demand throughout the configuration.
@dev
bits 232-255: The currency of the amount of overflow that a project is allowed to tap.
_projectId The ID of the project to get the packed overflow allowance data of.
_configuration The configuration during which the packed overflow allowance data applies.
_terminal The terminal managing the overflow.
_token The token for which overflow is being allowed.
*/
mapping(uint256 => mapping(uint256 => mapping(IJBPaymentTerminal => mapping(address => uint256))))
internal _packedOverflowAllowanceDataOf;
//*********************************************************************//
// --------------- public immutable stored properties ---------------- //
//*********************************************************************//
/**
@notice
Mints ERC-721's that represent project ownership.
*/
IJBProjects public immutable override projects;
/**
@notice
The contract storing all funding cycle configurations.
*/
IJBFundingCycleStore public immutable override fundingCycleStore;
/**
@notice
The contract that manages token minting and burning.
*/
IJBTokenStore public immutable override tokenStore;
/**
@notice
The contract that stores splits for each project.
*/
IJBSplitsStore public immutable override splitsStore;
/**
@notice
A contract that stores fund access constraints for each project.
*/
IJBFundAccessConstraintsStore public immutable override fundAccessConstraintsStore;
/**
@notice
The directory of terminals and controllers for projects.
*/
IJBDirectory public immutable override directory;
//*********************************************************************//
// --------------------- public stored properties -------------------- //
//*********************************************************************//
/**
@notice
The current undistributed reserved token balance of.
_projectId The ID of the project to get a reserved token balance of.
*/
mapping(uint256 => uint256) public override reservedTokenBalanceOf;
//*********************************************************************//
// ------------------------- external views -------------------------- //
//*********************************************************************//
/**
@notice
A project's funding cycle for the specified configuration along with its metadata.
@param _projectId The ID of the project to which the funding cycle belongs.
@return fundingCycle The funding cycle.
@return metadata The funding cycle's metadata.
*/
function getFundingCycleOf(uint256 _projectId, uint256 _configuration)
external
view
override
returns (JBFundingCycle memory fundingCycle, JBFundingCycleMetadata memory metadata)
{
fundingCycle = fundingCycleStore.get(_projectId, _configuration);
metadata = fundingCycle.expandMetadata();
}
/**
@notice
A project's latest configured funding cycle along with its metadata and the ballot state of the configuration.
@param _projectId The ID of the project to which the funding cycle belongs.
@return fundingCycle The latest configured funding cycle.
@return metadata The latest configured funding cycle's metadata.
@return ballotState The state of the configuration.
*/
function latestConfiguredFundingCycleOf(uint256 _projectId)
external
view
override
returns (
JBFundingCycle memory fundingCycle,
JBFundingCycleMetadata memory metadata,
JBBallotState ballotState
)
{
(fundingCycle, ballotState) = fundingCycleStore.latestConfiguredOf(_projectId);
metadata = fundingCycle.expandMetadata();
}
/**
@notice
A project's current funding cycle along with its metadata.
@param _projectId The ID of the project to which the funding cycle belongs.
@return fundingCycle The current funding cycle.
@return metadata The current funding cycle's metadata.
*/
function currentFundingCycleOf(uint256 _projectId)
external
view
override
returns (JBFundingCycle memory fundingCycle, JBFundingCycleMetadata memory metadata)
{
fundingCycle = fundingCycleStore.currentOf(_projectId);
metadata = fundingCycle.expandMetadata();
}
/**
@notice
A project's queued funding cycle along with its metadata.
@param _projectId The ID of the project to which the funding cycle belongs.
@return fundingCycle The queued funding cycle.
@return metadata The queued funding cycle's metadata.
*/
function queuedFundingCycleOf(uint256 _projectId)
external
view
override
returns (JBFundingCycle memory fundingCycle, JBFundingCycleMetadata memory metadata)
{
fundingCycle = fundingCycleStore.queuedOf(_projectId);
metadata = fundingCycle.expandMetadata();
}
//*********************************************************************//
// -------------------------- public views --------------------------- //
//*********************************************************************//
/**
@notice
Gets the current total amount of outstanding tokens for a project.
@param _projectId The ID of the project to get total outstanding tokens of.
@return The current total amount of outstanding tokens for the project.
*/
function totalOutstandingTokensOf(uint256 _projectId) public view override returns (uint256) {
// Add the reserved tokens to the total supply.
return tokenStore.totalSupplyOf(_projectId) + reservedTokenBalanceOf[_projectId];
}
/**
@notice
Indicates if this contract adheres to the specified interface.
@dev
See {IERC165-supportsInterface}.
@param _interfaceId The ID of the interface to check for adherance to.
*/
function supportsInterface(bytes4 _interfaceId)
public
view
virtual
override(ERC165, IERC165)
returns (bool)
{
return
_interfaceId == type(IJBController3_1).interfaceId ||
_interfaceId == type(IJBController3_0_1).interfaceId ||
_interfaceId == type(IJBMigratable).interfaceId ||
_interfaceId == type(IJBOperatable).interfaceId ||
super.supportsInterface(_interfaceId);
}
//*********************************************************************//
// ---------------------------- constructor -------------------------- //
//*********************************************************************//
/**
@param _operatorStore A contract storing operator assignments.
@param _projects A contract which mints ERC-721's that represent project ownership and transfers.
@param _directory A contract storing directories of terminals and controllers for each project.
@param _fundingCycleStore A contract storing all funding cycle configurations.
@param _tokenStore A contract that manages token minting and burning.
@param _splitsStore A contract that stores splits for each project.
@param _fundAccessConstraintsStore A contract that stores fund access constraints for each project.
*/
constructor(
IJBOperatorStore _operatorStore,
IJBProjects _projects,
IJBDirectory _directory,
IJBFundingCycleStore _fundingCycleStore,
IJBTokenStore _tokenStore,
IJBSplitsStore _splitsStore,
IJBFundAccessConstraintsStore _fundAccessConstraintsStore
) JBOperatable(_operatorStore) {
projects = _projects;
directory = _directory;
fundingCycleStore = _fundingCycleStore;
tokenStore = _tokenStore;
splitsStore = _splitsStore;
fundAccessConstraintsStore = _fundAccessConstraintsStore;
}
//*********************************************************************//
// --------------------- external transactions ----------------------- //
//*********************************************************************//
/**
@notice
Creates a project. This will mint an ERC-721 into the specified owner's account, configure a first funding cycle, and set up any splits.
@dev
Each operation within this transaction can be done in sequence separately.
@dev
Anyone can deploy a project on an owner's behalf.
@param _owner The address to set as the owner of the project. The project ERC-721 will be owned by this address.
@param _projectMetadata Metadata to associate with the project within a particular domain. This can be updated any time by the owner of the project.
@param _data Data that defines the project's first funding cycle. These properties will remain fixed for the duration of the funding cycle.
@param _metadata Metadata specifying the controller specific params that a funding cycle can have. These properties will remain fixed for the duration of the funding cycle.
@param _mustStartAtOrAfter The time before which the configured funding cycle cannot start.
@param _groupedSplits An array of splits to set for any number of groups.
@param _fundAccessConstraints An array containing amounts that a project can use from its treasury for each payment terminal. Amounts are fixed point numbers using the same number of decimals as the accompanying terminal. The `_distributionLimit` and `_overflowAllowance` parameters must fit in a `uint232`.
@param _terminals Payment terminals to add for the project.
@param _memo A memo to pass along to the emitted event.
@return projectId The ID of the project.
*/
function launchProjectFor(
address _owner,
JBProjectMetadata calldata _projectMetadata,
JBFundingCycleData calldata _data,
JBFundingCycleMetadata calldata _metadata,
uint256 _mustStartAtOrAfter,
JBGroupedSplits[] calldata _groupedSplits,
JBFundAccessConstraints[] calldata _fundAccessConstraints,
IJBPaymentTerminal[] memory _terminals,
string memory _memo
) external virtual override returns (uint256 projectId) {
// Keep a reference to the directory.
IJBDirectory _directory = directory;
// Mint the project into the wallet of the owner.
projectId = projects.createFor(_owner, _projectMetadata);
// Set this contract as the project's controller in the directory.
_directory.setControllerOf(projectId, address(this));
// Configure the first funding cycle.
uint256 _configuration = _configure(
projectId,
_data,
_metadata,
_mustStartAtOrAfter,
_groupedSplits,
_fundAccessConstraints
);
// Add the provided terminals to the list of terminals.
if (_terminals.length > 0) _directory.setTerminalsOf(projectId, _terminals);
emit LaunchProject(_configuration, projectId, _memo, msg.sender);
}
/**
@notice
Creates a funding cycle for an already existing project ERC-721.
@dev
Each operation within this transaction can be done in sequence separately.
@dev
Only a project owner or operator can launch its funding cycles.
@param _projectId The ID of the project to launch funding cycles for.
@param _data Data that defines the project's first funding cycle. These properties will remain fixed for the duration of the funding cycle.
@param _metadata Metadata specifying the controller specific params that a funding cycle can have. These properties will remain fixed for the duration of the funding cycle.
@param _mustStartAtOrAfter The time before which the configured funding cycle cannot start.
@param _groupedSplits An array of splits to set for any number of groups.
@param _fundAccessConstraints An array containing amounts that a project can use from its treasury for each payment terminal. Amounts are fixed point numbers using the same number of decimals as the accompanying terminal. The `_distributionLimit` and `_overflowAllowance` parameters must fit in a `uint232`.
@param _terminals Payment terminals to add for the project.
@param _memo A memo to pass along to the emitted event.
@return configuration The configuration of the funding cycle that was successfully created.
*/
function launchFundingCyclesFor(
uint256 _projectId,
JBFundingCycleData calldata _data,
JBFundingCycleMetadata calldata _metadata,
uint256 _mustStartAtOrAfter,
JBGroupedSplits[] calldata _groupedSplits,
JBFundAccessConstraints[] memory _fundAccessConstraints,
IJBPaymentTerminal[] memory _terminals,
string memory _memo
)
external
virtual
override
requirePermission(projects.ownerOf(_projectId), _projectId, JBOperations.RECONFIGURE)
returns (uint256 configuration)
{
// If there is a previous configuration, reconfigureFundingCyclesOf should be called instead
if (fundingCycleStore.latestConfigurationOf(_projectId) > 0)
revert FUNDING_CYCLE_ALREADY_LAUNCHED();
// Set this contract as the project's controller in the directory.
directory.setControllerOf(_projectId, address(this));
// Configure the first funding cycle.
configuration = _configure(
_projectId,
_data,
_metadata,
_mustStartAtOrAfter,
_groupedSplits,
_fundAccessConstraints
);
// Add the provided terminals to the list of terminals.
if (_terminals.length > 0) directory.setTerminalsOf(_projectId, _terminals);
emit LaunchFundingCycles(configuration, _projectId, _memo, msg.sender);
}
/**
@notice
Proposes a configuration of a subsequent funding cycle that will take effect once the current one expires if it is approved by the current funding cycle's ballot.
@dev
Only a project's owner or a designated operator can configure its funding cycles.
@param _projectId The ID of the project whose funding cycles are being reconfigured.
@param _data Data that defines the funding cycle. These properties will remain fixed for the duration of the funding cycle.
@param _metadata Metadata specifying the controller specific params that a funding cycle can have. These properties will remain fixed for the duration of the funding cycle.
@param _mustStartAtOrAfter The time before which the configured funding cycle cannot start.
@param _groupedSplits An array of splits to set for any number of groups.
@param _fundAccessConstraints An array containing amounts that a project can use from its treasury for each payment terminal. Amounts are fixed point numbers using the same number of decimals as the accompanying terminal. The `_distributionLimit` and `_overflowAllowance` parameters must fit in a `uint232`.
@param _memo A memo to pass along to the emitted event.
@return configuration The configuration of the funding cycle that was successfully reconfigured.
*/
function reconfigureFundingCyclesOf(
uint256 _projectId,
JBFundingCycleData calldata _data,
JBFundingCycleMetadata calldata _metadata,
uint256 _mustStartAtOrAfter,
JBGroupedSplits[] calldata _groupedSplits,
JBFundAccessConstraints[] calldata _fundAccessConstraints,
string calldata _memo
)
external
virtual
override
requirePermission(projects.ownerOf(_projectId), _projectId, JBOperations.RECONFIGURE)
returns (uint256 configuration)
{
// Configure the next funding cycle.
configuration = _configure(
_projectId,
_data,
_metadata,
_mustStartAtOrAfter,
_groupedSplits,
_fundAccessConstraints
);
emit ReconfigureFundingCycles(configuration, _projectId, _memo, msg.sender);
}
/**
@notice
Mint new token supply into an account, and optionally reserve a supply to be distributed according to the project's current funding cycle configuration.
@dev
Only a project's owner, a designated operator, one of its terminals, or the current data source can mint its tokens.
@param _projectId The ID of the project to which the tokens being minted belong.
@param _tokenCount The amount of tokens to mint in total, counting however many should be reserved.
@param _beneficiary The account that the tokens are being minted for.
@param _memo A memo to pass along to the emitted event.
@param _preferClaimedTokens A flag indicating whether a project's attached token contract should be minted if they have been issued.
@param _useReservedRate Whether to use the current funding cycle's reserved rate in the mint calculation.
@return beneficiaryTokenCount The amount of tokens minted for the beneficiary.
*/
function mintTokensOf(
uint256 _projectId,
uint256 _tokenCount,
address _beneficiary,
string calldata _memo,
bool _preferClaimedTokens,
bool _useReservedRate
) external virtual override returns (uint256 beneficiaryTokenCount) {
// There should be tokens to mint.
if (_tokenCount == 0) revert ZERO_TOKENS_TO_MINT();
// Define variables that will be needed outside scoped section below.
// Keep a reference to the reserved rate to use
uint256 _reservedRate;
// Scoped section prevents stack too deep. `_fundingCycle` only used within scope.
{
// Get a reference to the project's current funding cycle.
JBFundingCycle memory _fundingCycle = fundingCycleStore.currentOf(_projectId);
// Minting limited to: project owner, authorized callers, project terminal and current funding cycle data source
_requirePermissionAllowingOverride(
projects.ownerOf(_projectId),
_projectId,
JBOperations.MINT,
directory.isTerminalOf(_projectId, IJBPaymentTerminal(msg.sender)) ||
msg.sender == address(_fundingCycle.dataSource())
);
// If the message sender is not a terminal or a datasource, the current funding cycle must allow minting.
if (
!_fundingCycle.mintingAllowed() &&
!directory.isTerminalOf(_projectId, IJBPaymentTerminal(msg.sender)) &&
msg.sender != address(_fundingCycle.dataSource())
) revert MINT_NOT_ALLOWED_AND_NOT_TERMINAL_DELEGATE();
// Determine the reserved rate to use.
_reservedRate = _useReservedRate ? _fundingCycle.reservedRate() : 0;
// Override the claimed token preference with the funding cycle value.
_preferClaimedTokens = _preferClaimedTokens == true
? _preferClaimedTokens
: _fundingCycle.preferClaimedTokenOverride();
}
if (_reservedRate != JBConstants.MAX_RESERVED_RATE) {
// The unreserved token count that will be minted for the beneficiary.
beneficiaryTokenCount = PRBMath.mulDiv(
_tokenCount,
JBConstants.MAX_RESERVED_RATE - _reservedRate,
JBConstants.MAX_RESERVED_RATE
);
// Mint the tokens.
tokenStore.mintFor(_beneficiary, _projectId, beneficiaryTokenCount, _preferClaimedTokens);
}
// Add reserved tokens if needed
if (_reservedRate > 0)
reservedTokenBalanceOf[_projectId] += _tokenCount - beneficiaryTokenCount;
emit MintTokens(
_beneficiary,
_projectId,
_tokenCount,
beneficiaryTokenCount,
_memo,
_reservedRate,
msg.sender
);
}
/**
@notice
Burns a token holder's supply.
@dev
Only a token's holder, a designated operator, or a project's terminal can burn it.
@param _holder The account that is having its tokens burned.
@param _projectId The ID of the project to which the tokens being burned belong.
@param _tokenCount The number of tokens to burn.
@param _memo A memo to pass along to the emitted event.
@param _preferClaimedTokens A flag indicating whether a project's attached token contract should be burned first if they have been issued.
*/
function burnTokensOf(
address _holder,
uint256 _projectId,
uint256 _tokenCount,
string calldata _memo,
bool _preferClaimedTokens
)
external
virtual
override
requirePermissionAllowingOverride(
_holder,
_projectId,
JBOperations.BURN,
directory.isTerminalOf(_projectId, IJBPaymentTerminal(msg.sender))
)
{
// There should be tokens to burn
if (_tokenCount == 0) revert NO_BURNABLE_TOKENS();
// Get a reference to the project's current funding cycle.
JBFundingCycle memory _fundingCycle = fundingCycleStore.currentOf(_projectId);
// If the message sender is a terminal, the current funding cycle must not be paused.
if (
_fundingCycle.burnPaused() &&
!directory.isTerminalOf(_projectId, IJBPaymentTerminal(msg.sender))
) revert BURN_PAUSED_AND_SENDER_NOT_VALID_TERMINAL_DELEGATE();
// Burn the tokens.
tokenStore.burnFrom(_holder, _projectId, _tokenCount, _preferClaimedTokens);
emit BurnTokens(_holder, _projectId, _tokenCount, _memo, msg.sender);
}
/**
@notice
Distributes all outstanding reserved tokens for a project.
@param _projectId The ID of the project to which the reserved tokens belong.
@param _memo A memo to pass along to the emitted event.
@return The amount of minted reserved tokens.
*/
function distributeReservedTokensOf(uint256 _projectId, string calldata _memo)
external
virtual
override
returns (uint256)
{
return _distributeReservedTokensOf(_projectId, _memo);
}
/**
@notice
Allows other controllers to signal to this one that a migration is expected for the specified project.
@dev
This controller should not yet be the project's controller.
@param _projectId The ID of the project that will be migrated to this controller.
@param _from The controller being migrated from.
*/
function prepForMigrationOf(uint256 _projectId, address _from) external virtual override {
_projectId; // Prevents unused var compiler and natspec complaints.
_from; // Prevents unused var compiler and natspec complaints.
}
/**
@notice
Allows a project to migrate from this controller to another.
@dev
Only a project's owner or a designated operator can migrate it.
@param _projectId The ID of the project that will be migrated from this controller.
@param _to The controller to which the project is migrating.
*/
function migrate(uint256 _projectId, IJBMigratable _to)
external
virtual
override
requirePermission(projects.ownerOf(_projectId), _projectId, JBOperations.MIGRATE_CONTROLLER)
{
// Keep a reference to the directory.
IJBDirectory _directory = directory;
// This controller must be the project's current controller.
if (_directory.controllerOf(_projectId) != address(this)) revert NOT_CURRENT_CONTROLLER();
// Get a reference to the project's current funding cycle.
JBFundingCycle memory _fundingCycle = fundingCycleStore.currentOf(_projectId);
// Migration must be allowed.
if (!_fundingCycle.controllerMigrationAllowed()) revert MIGRATION_NOT_ALLOWED();
// All reserved tokens must be minted before migrating.
if (reservedTokenBalanceOf[_projectId] != 0) _distributeReservedTokensOf(_projectId, '');
// Make sure the new controller is prepped for the migration.
_to.prepForMigrationOf(_projectId, address(this));
// Set the new controller.
_directory.setControllerOf(_projectId, address(_to));
emit Migrate(_projectId, _to, msg.sender);
}
//*********************************************************************//
// ------------------------ internal functions ----------------------- //
//*********************************************************************//
/**
@notice
Distributes all outstanding reserved tokens for a project.
@param _projectId The ID of the project to which the reserved tokens belong.
@param _memo A memo to pass along to the emitted event.
@return tokenCount The amount of minted reserved tokens.
*/
function _distributeReservedTokensOf(uint256 _projectId, string memory _memo)
internal
returns (uint256 tokenCount)
{
// Keep a reference to the token store.
IJBTokenStore _tokenStore = tokenStore;
// Get the current funding cycle to read the reserved rate from.
JBFundingCycle memory _fundingCycle = fundingCycleStore.currentOf(_projectId);
// Get a reference to the number of tokens that need to be minted.
tokenCount = reservedTokenBalanceOf[_projectId];
// Reset the reserved token balance
reservedTokenBalanceOf[_projectId] = 0;
// Get a reference to the project owner.
address _owner = projects.ownerOf(_projectId);
// Distribute tokens to splits and get a reference to the leftover amount to mint after all splits have gotten their share.
uint256 _leftoverTokenCount = tokenCount == 0
? 0
: _distributeToReservedTokenSplitsOf(
_projectId,
_fundingCycle.configuration,
JBSplitsGroups.RESERVED_TOKENS,
tokenCount
);
// Mint any leftover tokens to the project owner.
if (_leftoverTokenCount > 0)
_tokenStore.mintFor(_owner, _projectId, _leftoverTokenCount, false);
emit DistributeReservedTokens(
_fundingCycle.configuration,
_fundingCycle.number,
_projectId,
_owner,
tokenCount,
_leftoverTokenCount,
_memo,
msg.sender
);
}
/**
@notice
Distribute tokens to the splits according to the specified funding cycle configuration.
@param _projectId The ID of the project for which reserved token splits are being distributed.
@param _domain The domain of the splits to distribute the reserved tokens between.
@param _group The group of the splits to distribute the reserved tokens between.
@param _amount The total amount of tokens to mint.
@return leftoverAmount If the splits percents dont add up to 100%, the leftover amount is returned.
*/
function _distributeToReservedTokenSplitsOf(
uint256 _projectId,
uint256 _domain,
uint256 _group,
uint256 _amount
) internal returns (uint256 leftoverAmount) {
// Keep a reference to the token store.
IJBTokenStore _tokenStore = tokenStore;
// Set the leftover amount to the initial amount.
leftoverAmount = _amount;
// Get a reference to the project's reserved token splits.
JBSplit[] memory _splits = splitsStore.splitsOf(_projectId, _domain, _group);
//Transfer between all splits.
for (uint256 _i; _i < _splits.length; ) {
// Get a reference to the split being iterated on.
JBSplit memory _split = _splits[_i];
// The amount to send towards the split.
uint256 _tokenCount = PRBMath.mulDiv(
_amount,
_split.percent,
JBConstants.SPLITS_TOTAL_PERCENT
);
// Mints tokens for the split if needed.
if (_tokenCount > 0) {
_tokenStore.mintFor(
// If an allocator is set in the splits, set it as the beneficiary.
// Otherwise if a projectId is set in the split, set the project's owner as the beneficiary.
// If the split has a beneficiary send to the split's beneficiary. Otherwise send to the msg.sender.
_split.allocator != IJBSplitAllocator(address(0))
? address(_split.allocator)
: _split.projectId != 0
? projects.ownerOf(_split.projectId)
: _split.beneficiary != address(0)
? _split.beneficiary
: msg.sender,
_projectId,
_tokenCount,
_split.preferClaimed
);
// If there's an allocator set, trigger its `allocate` function.
if (_split.allocator != IJBSplitAllocator(address(0)))
_split.allocator.allocate(
JBSplitAllocationData(
address(_tokenStore.tokenOf(_projectId)),
_tokenCount,
18,
_projectId,
_group,
_split
)
);
// Subtract from the amount to be sent to the beneficiary.
leftoverAmount = leftoverAmount - _tokenCount;
}
emit DistributeToReservedTokenSplit(
_projectId,
_domain,
_group,
_split,
_tokenCount,
msg.sender
);
unchecked {
++_i;
}
}
}
/**
@notice
Configures a funding cycle and stores information pertinent to the configuration.
@param _projectId The ID of the project whose funding cycles are being reconfigured.
@param _data Data that defines the funding cycle. These properties will remain fixed for the duration of the funding cycle.
@param _metadata Metadata specifying the controller specific params that a funding cycle can have. These properties will remain fixed for the duration of the funding cycle.
@param _mustStartAtOrAfter The time before which the configured funding cycle cannot start.
@param _groupedSplits An array of splits to set for any number of groups.
@param _fundAccessConstraints An array containing amounts that a project can use from its treasury for each payment terminal. Amounts are fixed point numbers using the same number of decimals as the accompanying terminal.
@return configuration The configuration of the funding cycle that was successfully reconfigured.
*/
function _configure(
uint256 _projectId,
JBFundingCycleData calldata _data,
JBFundingCycleMetadata calldata _metadata,
uint256 _mustStartAtOrAfter,
JBGroupedSplits[] memory _groupedSplits,
JBFundAccessConstraints[] memory _fundAccessConstraints
) internal returns (uint256) {
// Make sure the provided reserved rate is valid.
if (_metadata.reservedRate > JBConstants.MAX_RESERVED_RATE) revert INVALID_RESERVED_RATE();
// Make sure the provided redemption rate is valid.
if (_metadata.redemptionRate > JBConstants.MAX_REDEMPTION_RATE)
revert INVALID_REDEMPTION_RATE();
// Make sure the provided ballot redemption rate is valid.
if (_metadata.ballotRedemptionRate > JBConstants.MAX_REDEMPTION_RATE)
revert INVALID_BALLOT_REDEMPTION_RATE();
// Configure the funding cycle's properties.
JBFundingCycle memory _fundingCycle = fundingCycleStore.configureFor(
_projectId,
_data,
JBFundingCycleMetadataResolver.packFundingCycleMetadata(_metadata),
_mustStartAtOrAfter
);
// Set splits for the group.
splitsStore.set(_projectId, _fundingCycle.configuration, _groupedSplits);
// Set the funds access constraints.
fundAccessConstraintsStore.setFor(
_projectId,
_fundingCycle.configuration,
_fundAccessConstraints
);
return _fundingCycle.configuration;
}
}