@@ -4,6 +4,7 @@ pragma solidity ^0.8.20;
44import "./NameConversion.sol " ;
55import {Ownable} from "../lib/openzeppelin-contracts/contracts/access/Ownable.sol " ;
66import "../lib/openzeppelin-contracts/contracts/utils/Strings.sol " ;
7+ import {console} from "../lib/forge-std/src/console.sol " ;
78
89/**
910 * @dev Minimal interface to the STLOS Vault (ERC4626).
@@ -27,6 +28,8 @@ contract BPVoteManager is Ownable {
2728 // ========== State ==========
2829
2930 ISTLOSVault public stlosVault;
31+ uint256 public decayStartEpoch;
32+ uint256 public decayIncreaseYearly;
3033
3134 // Aggregated "weighted TLOS" votes per BP
3235 mapping (uint64 => uint256 ) public totalVotes;
@@ -63,6 +66,15 @@ contract BPVoteManager is Ownable {
6366 stlosVault = ISTLOSVault (_stlosVault);
6467 }
6568
69+ // ========== Owner Functions ==========
70+ function setDecayStartEpoch (uint256 _decayStartEpoch ) external onlyOwner {
71+ decayStartEpoch = _decayStartEpoch;
72+ }
73+
74+ function setDecayIncreaseYearly (uint256 _decayIncreaseYearly ) external onlyOwner {
75+ decayIncreaseYearly = _decayIncreaseYearly;
76+ }
77+
6678 // ========== BP Management ==========
6779
6880 function registerBP (uint64 bpName ) external onlyOwner {
@@ -212,6 +224,9 @@ contract BPVoteManager is Ownable {
212224 // Inverse weighting
213225 uint256 weightedTlos = inverseVoteWeight (tlosAmount, bps.length );
214226
227+ // Vote decay multiplier
228+ weightedTlos = applyDecayMultiplier (weightedTlos);
229+
215230 // Add aggregator
216231 for (uint256 i = 0 ; i < bps.length ; i++ ) {
217232 totalVotes[bps[i]] += weightedTlos;
@@ -318,6 +333,48 @@ contract BPVoteManager is Ownable {
318333 return weighted;
319334 }
320335
336+ /**
337+ * @dev Apply the decay multiplier to the weighted TLOS.
338+ * The decay multiplier follows the below code from native C++ side:
339+
340+ auto inverse_weighted_vote = inverse_vote_weight((double)totalStaked, (double) producers.size());
341+
342+ // TODO: read these from a singleton config table
343+ auto decay_start_epoch = 1743739705;
344+ auto decay_increase_step = 0.0000001;
345+ auto decay_increase_interval_sec = 60;
346+
347+ auto new_vote_weight = inverse_weighted_vote;
348+
349+ if (decay_start_epoch > 0) {
350+ auto decay_increase = (current_time_point().sec_since_epoch() - decay_start_epoch) / decay_increase_interval_sec;
351+ auto decay_multiplier = 1 + (decay_increase * decay_increase_step);
352+ new_vote_weight = inverse_weighted_vote * decay_multiplier;
353+ }
354+ */
355+ function applyDecayMultiplier (uint256 weightedTlos ) public view returns (uint256 ) {
356+ if (decayStartEpoch == 0 || block .timestamp <= decayStartEpoch) {
357+ return weightedTlos;
358+ }
359+
360+ // Get current time in seconds since epoch
361+ uint256 currentTime = block .timestamp ;
362+ uint256 secondsOfDecay = currentTime - decayStartEpoch;
363+
364+ // Calculate decay increase
365+ // console.log("Current time: %s", currentTime);
366+ // console.log("Decay increase yearly: %s", decayIncreaseYearly);
367+ // console.log("Seconds of decay: %s", secondsOfDecay);
368+ // 31536000 seconds per year
369+ uint256 decayMultiplier = 1e18 + ((decayIncreaseYearly * secondsOfDecay) / 31536000 );
370+ // console.log("Decay multiplier: %s", decayMultiplier);
371+
372+
373+ // Apply decay multiplier
374+ uint256 newVoteWeight = Math.mulDiv (weightedTlos, decayMultiplier, 1e18 );
375+ return newVoteWeight;
376+ }
377+
321378 /**
322379 * @dev Convert a uint256 to decimal string (from OpenZeppelin Strings).
323380 */
0 commit comments