Skip to main content


Contract: JBFundingCycleStore​‌

Interface: IJBFundingCycleStore

Configures the next eligible funding cycle for the specified project.

Only a project's current controller can configure its funding cycles.


function configureFor(
uint256 _projectId,
JBFundingCycleData calldata _data,
uint256 _metadata,
uint256 _mustStartAtOrAfter
) external override onlyController(_projectId) returns (JBFundingCycle memory) { ... }
  • Arguments:
    • _projectId is the ID of the project being configured.
    • _data is the JBFundingCycleData for the configuration.
    • _metadata is arbitrary extra data to associate with this funding cycle configuration that's not used within.
    • _mustStartAtOrAfter is the time before which the initialized funding cycle cannot start.
  • Through the onlyController modifier, the function can only be accessed by the controller of the _projectId.
  • The function overrides a function definition from the IJBFundingCycleStore interface.
  • Returns the JBFundingCycle that the configuration will take effect during..


  1. Make sure the duration fits in a uint32.

    // Duration must fit in a uint32.
    if (_data.duration > type(uint32).max) revert INVALID_DURATION();
  2. Make sure the _data.discountRate is less than the expected maximum value.

    // Discount rate must be less than or equal to 100%.
    if (_data.discountRate > JBConstants.MAX_DISCOUNT_RATE) revert INVALID_DISCOUNT_RATE();

    Library references:

  3. Make sure the _data.weight fits in a uint80.

    // Weight must fit into a uint88.
    if (_data.weight > type(uint88).max) revert INVALID_WEIGHT();
  4. Set the time at which the funding cycle must start on or after as the block's timestamp if a time in the past was specified.

    // If the start date is in the past, set it to be the current timestamp.
    if (_mustStartAtOrAfter < block.timestamp) _mustStartAtOrAfter = block.timestamp;
  5. Make sure the soonest time the queued funding cycle can start still fits in a uint56.

    // Make sure the min start date fits in a uint56, and that the start date of an upcoming cycle also starts within the max.
    if (_mustStartAtOrAfter + _data.duration > type(uint56).max) revert INVALID_TIMEFRAME();
  6. Make sure the provided ballot is valid.

    // Ballot should be a valid contract, supporting the correct interface
    if (_data.ballot != IJBFundingCycleBallot(address(0))) {
    address _ballot = address(_data.ballot);

    // No contract at the address ?
    if (_ballot.code.length == 0) revert INVALID_BALLOT();

    // Make sure the ballot supports the expected interface.
    try _data.ballot.supportsInterface(type(IJBFundingCycleBallot).interfaceId) returns (
    bool _supports
    ) {
    if (!_supports) revert INVALID_BALLOT(); // Contract exists at the address but with the wrong interface
    } catch {
    revert INVALID_BALLOT(); // No ERC165 support

    External references:

  7. Get a reference to the time at which the configuration is occurring.

    // The configuration timestamp is now.
    uint256 _configuration = block.timestamp;
  8. Configure the intrinsic properties. This'll create a new funding cycle if there isn't a queued one already.

    // Set up a reconfiguration by configuring intrinsic properties.
    // Must start on or after the current timestamp.
    _mustStartAtOrAfter > block.timestamp ? _mustStartAtOrAfter : block.timestamp

    Internal references:

  9. Store all of the user configuration properties provided. These properties can all be packed into one uint256 storage slot. No need to store if the resulting stored value would be 0 since the storage slot defaults to 0.

    // Efficiently stores a funding cycles provided user defined properties.
    // If all user config properties are zero, no need to store anything as the default value will have the same outcome.
    if (
    _data.ballot != IJBFundingCycleBallot(address(0)) ||
    _data.duration > 0 ||
    _data.discountRate > 0
    ) {
    // ballot in bits 0-159 bytes.
    uint256 packed = uint160(address(_data.ballot));

    // duration in bits 160-191 bytes.
    packed |= _data.duration << 160;

    // discountRate in bits 192-223 bytes.
    packed |= _data.discountRate << 192;

    // Set in storage.
    _packedUserPropertiesOf[_projectId][_configuration] = packed;

    Internal references:

  10. Store the provided metadata for the configuration. No need to store if the value is 0 since the storage slot defaults to 0.

    // Set the metadata if needed.
    if (_metadata > 0) _metadataOf[_projectId][_configuration] = _metadata;

    Internal references:

  11. Emit a Configure event with the relevant parameters.

    emit Configure(_configuration, _projectId, _data, _metadata, _mustStartAtOrAfter, msg.sender);

    Event references:

* [`Configure`](/dev/api/contracts/jbfundingcyclestore/events/configure)
  1. Return the JBFundingCycle struct that carries the new configuration.

    // Return the funding cycle for the new configuration.
    return _getStructFor(_projectId, _configuration);

    Internal references:

* [`_getStructFor`](/dev/api/contracts/jbfundingcyclestore/read/-_getstructfor)