Skip to main content


Contract: JBSplitsStore​‌

Interface: IJBSplitsStore

Sets a project's splits.

The new splits must include any currently set splits that are locked.


function _set(
uint256 _projectId,
uint256 _domain,
uint256 _group,
JBSplit[] memory _splits
) internal { ... }
  • Arguments:
    • _projectId is the ID of the project for which splits are being added.
    • _domain is an identifier within which the splits should be considered active.
    • _group is an identifier between of splits being set. All splits within this _group must add up to within 100%.
    • _splits are the JBSplits to set.
  • The resulting function is internal to this contract and its inheriters.
  • The function doesn't return anything.


  1. Get a reference to the current splits set for the specified project's domain, within the specified group.

    // Get a reference to the project's current splits.
    JBSplit[] memory _currentSplits = _getStructsFor(_projectId, _domain, _group);

    Internal references:

  2. Keep a reference to the number of current splits.

    // Keep a reference to the number of splits.
    uint256 _currentSplitsLength = _currentSplits.length;
  3. Loop through each current split to make sure the new splits being set respect any current split bound by a lock constraint.

    // Check to see if all locked splits are included.
    for (uint256 _i; _i < _currentSplitsLength; ) {
    1. Make sure the current split isn't locked if it's being removed.

      if (
      block.timestamp < _currentSplits[_i].lockedUntil &&
      !_includesLocked(_splits, _currentSplits[_i])

      Internal references:

    2. Increment the loop counter.

      unchecked {
  4. Store a local variable to keep track of all the percents from the splits.

    // Add up all the percents to make sure they cumulative are under 100%.
    uint256 _percentTotal;
  5. Keep a reference to the number of splits being set.

    // Keep a reference to the number of splits.
    uint256 _splitsLength = _splits.length;
  6. Loop through each newly provided splits to validate the provided properties.

    for (uint256 _i; _i < _splitsLength; ) { ... }
    1. Check that the percent for the current split is not zero.

      // The percent should be greater than 0.
      if (_splits[_i].percent == 0) revert INVALID_SPLIT_PERCENT();
    2. Check that the ID of the project for the current split is within the max value that can be packed.

      // ProjectId should be within a uint56
      if (_splits[_i].projectId > type(uint56).max) revert INVALID_PROJECT_ID();
    3. Increment the total percents that have been accumulated so far.

      // Add to the total percents.
      _percentTotal = _percentTotal + _splits[_i].percent;
    4. Make sure the accumulated percents are under 100%.

      // Validate the total does not exceed the expected value.
      if (_percentTotal > JBConstants.SPLITS_TOTAL_PERCENT) revert INVALID_TOTAL_PERCENT();

      Library references:

    5. Pack common split properties into a storage slot.

      // Pack the first split part properties.
      uint256 _packedSplitParts1;

      // prefer claimed in bit 0.
      if (_splits[_i].preferClaimed) _packedSplitParts1 = 1;
      // prefer add to balance in bit 1.
      if (_splits[_i].preferAddToBalance) _packedSplitParts1 |= 1 << 1;
      // percent in bits 2-33.
      _packedSplitParts1 |= _splits[_i].percent << 2;
      // projectId in bits 32-89.
      _packedSplitParts1 |= _splits[_i].projectId << 34;
      // beneficiary in bits 90-249.
      _packedSplitParts1 |= uint256(uint160(address(_splits[_i].beneficiary))) << 90;

      // Store the first split part.
      _packedSplitParts1Of[_projectId][_domain][_group][_i] = _packedSplitParts1;

      Internal references:

    6. Pack less common split properties into another storage slot if needed. Otherwise, delete any content in storage at the index being iterated on.

      // If there's data to store in the second packed split part, pack and store.
      if (_splits[_i].lockedUntil > 0 || _splits[_i].allocator != IJBSplitAllocator(address(0))) {
      // Locked until should be within a uint48
      if (_splits[_i].lockedUntil > type(uint48).max) revert INVALID_LOCKED_UNTIL();

      // lockedUntil in bits 0-47.
      uint256 _packedSplitParts2 = uint48(_splits[_i].lockedUntil);
      // allocator in bits 48-207.
      _packedSplitParts2 |= uint256(uint160(address(_splits[_i].allocator))) << 48;

      // Store the second split part.
      _packedSplitParts2Of[_projectId][_domain][_group][_i] = _packedSplitParts2;

      // Otherwise if there's a value stored in the indexed position, delete it.
      } else if (_packedSplitParts2Of[_projectId][_domain][_group][_i] > 0)
      delete _packedSplitParts2Of[_projectId][_domain][_group][_i];

      Internal references:

    1. For each added split, emit a SetSplit event with all relevant parameters.

      emit SetSplit(_projectId, _domain, _group, _splits[_i], msg.sender);

      Event references:

    2. Increment the loop counter.

      unchecked {
  7. Store the new array length.

    // Set the new length of the splits.
    _splitCountOf[_projectId][_domain][_group] = _splitsLength;

    Internal references: