Skip to content

Conversation

@xavikh
Copy link
Contributor

@xavikh xavikh commented Aug 27, 2025

Files to focus on to review:

  • src/allocatorStrategies/GaugeDistributionStrategy.sol
  • src/helpers/GaugeVoterSnapshotter.sol

xavikh added 15 commits August 27, 2025 04:37
This commit modifies the allocator strategy to distribute tokens to
gauges based on votes received rather than to voters based on voting
power. The same proportional allocation logic is used but applied to
gauge vote counts instead of voter powers.
The change adds proper validation to prevent setting distributions for
past epochs that have no snapshot, while allowing updates to snapshotted
epochs.
The strategy now relies on external claim tracking rather than storing
claim data internally
The commit modifies the allocation strategy to use live data from the
gauge voter contract for current epoch calculations, while still using
snapshots for past epochs.
The tests analyze gas usage patterns for claims across different epochs
and distributions, helping measure and optimize performance.
The commit adds a sum to optimize gas costs for calculating past epochs'
distributions. Key changes:

- Implement sum processing when setting epoch distributions - Optimize
getClaimeableAmount by using sum values for past epochs - Add tests for
sum functionality and gas optimization
@xavikh xavikh changed the title Feature/rd 1865 gauge example with ongoing distributions [RD-1865] Gauge continuous distributions Aug 29, 2025
@xavikh xavikh requested review from brickpop and carlosgj94 August 29, 2025 09:46
Comment on lines +58 to +59
if (msg.sender != owner() && msg.sender != address(dao())) {
revert IAllocatorStrategy.OnlyDAOAllowed(msg.sender);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

owner() or dao() doesn't match OnlyDAOAllowed()

super.initialize(_strategyTypeId, _dao, _plugin, _auxData);

IGaugeVoterSnapshotter _snapshotter = abi.decode(_auxData, (IGaugeVoterSnapshotter));
if (address(_snapshotter) == address(0)) revert InvalidSnapshotter();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional checks like code.size or supportsInterface() might help

Comment on lines +89 to +101
function getInitializationEncodingTypes() external pure override returns (string memory types) {
return "address"; // IGaugeVoterSnapshotter
}

/// @inheritdoc IAllocatorStrategy
function getCreationEncodingTypes() external pure override returns (string memory types) {
return "uint256,uint256"; // startEpoch, endEpoch
}

/// @inheritdoc IAllocatorStrategy
function getClaimEncodingTypes() external pure override returns (string memory types) {
return ""; // No auxData needed for claims since we accumulate all epochs
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As recommended earlier, I would provide a single source of truth (i.e. encodeInitializeParams(), decodeInitializeParams(), encodeNewEncodingTypes, ...) rather than shipping ABI getters that the UI must handle on its own and which may add friction/mismatches

GaugeDistributionCampaign storage campaign = campaigns[_campaignId];

// Validate campaign exists
_validateCampaignExists(_campaignId);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_assertCampaignExists()

uint256 startFrom = _getNextUnprocessedEpoch(campaign);

// Only calculate epochs not yet processed
for (uint256 epoch = startFrom; epoch <= endEpoch;) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unbound loops may lead to gas limit issues in the future.

  • Why are we looping if we mainly care about epoch == currentEpoch?
  • What is snapshotter.isEpochSnapshotted() actually doing?

/// @notice Validates that a past epoch has a snapshot
/// @param _epochId Epoch to validate
/// @param _currentEpoch Current epoch
function _validatePastEpochHasSnapshot(uint256 _epochId, uint256 _currentEpoch) internal view {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_assertEpochSnapshotted()

Comment on lines +488 to +491
_setEpochDistribution(campaign, _epochId, _amount, currentEpoch);
}

function _setEpochDistribution(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set epoch distribution, what is the "distribution" being set?
Consider adding natspec comments that explain what this function is about

/// @title GaugeVoterSnapshotter
/// @notice Contract that captures and stores historical gauge voting data
/// @dev Stores snapshots of gauge votes and total voting power for each epoch
contract GaugeVoterSnapshotter is IGaugeVoterSnapshotter, DaoAuthorizableUpgradeable {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why using DaoAuthorizableUpgradeable if there is no auth() guard or dao() getter?

ERC165Upgradeable
{
/// @notice The ID of the permission required to manage encoder configurations
bytes32 public constant ENCODER_MANAGER_PERMISSION_ID = keccak256("ENCODER_MANAGER_PERMISSION");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this permission used?

ERC165Upgradeable
{
/// @notice The ID of the permission required to manage strategy configurations
bytes32 public constant STRATEGY_MANAGER_PERMISSION_ID = keccak256("STRATEGY_MANAGER_PERMISSION");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this permission used?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants