Skip to content

fix: add quadratic scaling penalty to TimeWeightedVotingPower (#91)#125

Open
RonTuretzky wants to merge 4 commits into
RonTuretzky/108-dust-reentrancyfrom
RonTuretzky/107-twvp-scaling
Open

fix: add quadratic scaling penalty to TimeWeightedVotingPower (#91)#125
RonTuretzky wants to merge 4 commits into
RonTuretzky/108-dust-reentrancyfrom
RonTuretzky/107-twvp-scaling

Conversation

@RonTuretzky
Copy link
Copy Markdown
Contributor

Summary

  • Add optional quadratic scaling penalty for flash-loan mitigation
  • When scalingPeriod > 0, intervals shorter than period receive penalty: contribution = value * duration^2 / scalingPeriod
  • Add setScalingPeriod() with owner-only access and MAX_SCALING_PERIOD guard
  • Rename immutables to SCREAMING_SNAKE_CASE (VOTING_TOKEN, CYCLE_MODULE)
  • 8 new test scenarios covering penalty calculations and access control

Closes #91
Supersedes #107

Stack: PR 8 of 10 (0.0.2) — stacked on #124

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds an optional per-checkpoint-interval quadratic scaling penalty to TimeWeightedVotingPower to better mitigate flash-loan voting power spikes, along with an owner-controlled scalingPeriod configuration and accompanying tests.

Changes:

  • Add scalingPeriod (with MAX_SCALING_PERIOD cap), setScalingPeriod() (owner-only), and apply per-interval quadratic penalty in _calculateTimeWeightedPower.
  • Rename strategy immutables to VOTING_TOKEN and CYCLE_MODULE and update call sites accordingly.
  • Extend the test suite with multiple scenarios covering penalty behavior and access control; update deployment script constructor args.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/implementation/TimeWeightedVotingPower.sol Implements scaling-period feature, admin setter, max guard, and immutable renames.
test/TimeWeightedVotingPower.t.sol Updates constructor/getter usage and adds new scaling-period/access-control tests.
script/Deploy.s.sol Updates deployment to pass initial scalingPeriod argument.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread script/Deploy.s.sol
Comment on lines 208 to +210
// 4. Voting power strategy (uses constructor immutables, deployed directly)
votingPowerStrategy =
address(new TimeWeightedVotingPower(IVotesCheckpoints(token), AbstractCycleModule(cycleModule)));
address(new TimeWeightedVotingPower(IVotesCheckpoints(token), AbstractCycleModule(cycleModule), 0));
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

TimeWeightedVotingPower now has an owner-only setScalingPeriod(), but the deploy script deploys the strategy directly (owner becomes the broadcasting EOA) and never transfers ownership to p.owner. This leaves the deployer with ongoing control over voting power behavior and prevents the intended system owner from managing scalingPeriod. Consider transferring strategy ownership to p.owner after deployment (or accepting an _owner constructor arg).

Copilot uses AI. Check for mistakes.
///
/// This makes flash-loan attacks progressively more expensive even when the
/// total period is long — an attacker holding tokens for only 1 block with
/// scalingPeriod=10 gets ~100x less voting power than the nominal amount.
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

NatSpec example is numerically incorrect: for a 1-block hold the reduction factor is 1/scalingPeriod. With scalingPeriod=10, power should be ~10x less (not ~100x). Please update the comment/example to match the implemented formula and/or use scalingPeriod=100 in the example if you want ~100x reduction.

Suggested change
/// scalingPeriod=10 gets ~100x less voting power than the nominal amount.
/// scalingPeriod=10 gets ~10x less voting power than the nominal amount.

Copilot uses AI. Check for mistakes.
/// @notice Thrown when scaling period exceeds the maximum allowed value
error ScalingPeriodTooLarge();

/// @notice Maximum allowed scaling period (~30 days in blocks at 12s/block)
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

The constant value and its comment disagree: MAX_SCALING_PERIOD = 365 days / 12 corresponds to ~365 days worth of 12s blocks, not ~30 days. Either update the comment to ~365 days or change the constant to 30 days / 12 if the intended cap is 30 days.

Suggested change
/// @notice Maximum allowed scaling period (~30 days in blocks at 12s/block)
/// @notice Maximum allowed scaling period (~365 days in blocks at 12s/block)

Copilot uses AI. Check for mistakes.
Comment on lines +445 to +447
// Test 3: No penalty when intervalLength exactly equals scalingPeriod
function testNoPenaltyWhenIntervalExceedsScalingPeriod() public {
// scalingPeriod=100, intervalLength=100 => factor = 100/100 = 1.0 => no reduction
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

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

Test name says "Exceeds" but the scenario/documentation asserts the boundary case intervalLength == scalingPeriod. Rename the test (or adjust the scenario) so the name matches the behavior being validated.

Copilot uses AI. Check for mistakes.
@RonTuretzky RonTuretzky force-pushed the RonTuretzky/108-dust-reentrancy branch from 8545c16 to 8f03b58 Compare April 16, 2026 01:28
Tranquil-Flow and others added 4 commits April 15, 2026 21:28
Fix time-weighted voting power scaling to use the correct period
denominator, preventing inflated or deflated power values when
the scaling window does not align with cycle boundaries.
@RonTuretzky RonTuretzky force-pushed the RonTuretzky/107-twvp-scaling branch from 55b1a25 to 69057ed Compare April 16, 2026 01:29
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