Skip to content

Commit

Permalink
chore: init
Browse files Browse the repository at this point in the history
  • Loading branch information
Ron Turetzky committed Sep 23, 2024
1 parent 05aa9cd commit 47bdbff
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 3 deletions.
63 changes: 63 additions & 0 deletions src/VotingMultipliers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// VotingMultipliers.sol
pragma solidity ^0.8.22;

import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "./IMultiplier.sol";
import "../interfaces/IBread.sol";

contract VotingMultipliers is Initializable, OwnableUpgradeable {
IBread public BREAD;
IMultiplier[] public whitelistedMultipliers;
IMultiplier[] public queuedMultipliersForAddition;
IMultiplier[] public queuedMultipliersForRemoval;

event MultiplierAdded(IMultiplier indexed multiplier);
event MultiplierRemoved(IMultiplier indexed multiplier);

function initialize(IBread _bread) public initializer {
__Ownable_init();
BREAD = _bread;
}

function getTotalMultipliers(address user) external view returns (uint256) {
uint256 totalMultiplier = 1e18; // Start with 100% (no multiplier)
for (uint256 i = 0; i < whitelistedMultipliers.length; i++) {
IMultiplier multiplier = whitelistedMultipliers[i];
if (block.number <= multiplier.validUntil(user)) {
totalMultiplier += multiplier.getMultiplyingFactor(user);
}
}
return totalMultiplier;
}

function queueMultiplierAddition(IMultiplier _multiplier) external onlyOwner {
queuedMultipliersForAddition.push(_multiplier);
}

function queueMultiplierRemoval(IMultiplier _multiplier) external onlyOwner {
queuedMultipliersForRemoval.push(_multiplier);
}

function updateMultipliers() external onlyOwner {
// Add queued multipliers
for (uint256 i = 0; i < queuedMultipliersForAddition.length; i++) {
whitelistedMultipliers.push(queuedMultipliersForAddition[i]);
emit MultiplierAdded(queuedMultipliersForAddition[i]);
}
delete queuedMultipliersForAddition;

// Remove queued multipliers
for (uint256 i = 0; i < queuedMultipliersForRemoval.length; i++) {
for (uint256 j = 0; j < whitelistedMultipliers.length; j++) {
if (whitelistedMultipliers[j] == queuedMultipliersForRemoval[i]) {
whitelistedMultipliers[j] = whitelistedMultipliers[whitelistedMultipliers.length - 1];
whitelistedMultipliers.pop();
emit MultiplierRemoved(queuedMultipliersForRemoval[i]);
break;
}
}
}
delete queuedMultipliersForRemoval;
}
}
14 changes: 11 additions & 3 deletions src/YieldDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {ERC20VotesUpgradeable} from
import {Bread} from "bread-token/src/Bread.sol";

import {IYieldDistributor} from "src/interfaces/IYieldDistributor.sol";
import {VotingMultipliers} from "src/VotingMultipliers.sol";

/**
* @title Breadchain Yield Distributor
Expand Down Expand Up @@ -51,6 +52,8 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
uint256 public yieldFixedSplitDivisor;
/// @notice The address of the `ButteredBread` token contract
ERC20VotesUpgradeable public BUTTERED_BREAD;
/// @notice The address of the `VotingMultipliers` contract
VotingMultipliers public votingMultipliers;

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
Expand All @@ -66,7 +69,8 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
uint256 _cycleLength,
uint256 _yieldFixedSplitDivisor,
uint256 _lastClaimedBlockNumber,
address[] memory _projects
address[] memory _projects,
VotingMultipliers _votingMultipliers
) public initializer {
__Ownable_init(msg.sender);
if (
Expand All @@ -85,6 +89,7 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
cycleLength = _cycleLength;
yieldFixedSplitDivisor = _yieldFixedSplitDivisor;
lastClaimedBlockNumber = _lastClaimedBlockNumber;
votingMultipliers = _votingMultipliers;

projectDistributions = new uint256[](_projects.length);
projects = new address[](_projects.length);
Expand Down Expand Up @@ -249,18 +254,21 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable {
}
if (_totalPoints == 0) revert ZeroVotePoints();

uint256 multiplier = votingMultipliers.getTotalMultipliers(_account);
uint256 adjustedVotingPower = (_votingPower * multiplier) / 1e18;

bool _hasVotedInCycle = accountLastVoted[_account] > lastClaimedBlockNumber;
uint256[] storage _voterDistributions = voterDistributions[_account];
if (!_hasVotedInCycle) {
delete voterDistributions[_account];
currentVotes += _votingPower;
currentVotes += adjustedVotingPower;
}

for (uint256 i; i < _points.length; ++i) {
if (!_hasVotedInCycle) _voterDistributions.push(0);
else projectDistributions[i] -= _voterDistributions[i];

uint256 _currentProjectDistribution = ((_points[i] * _votingPower * PRECISION) / _totalPoints) / PRECISION;
uint256 _currentProjectDistribution = ((_points[i] * adjustedVotingPower * PRECISION) / _totalPoints) / PRECISION;
projectDistributions[i] += _currentProjectDistribution;
_voterDistributions[i] = _currentProjectDistribution;
}
Expand Down
82 changes: 82 additions & 0 deletions src/interfaces/IMultiplier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// IMultiplier.sol
pragma solidity ^0.8.22;

interface IMultiplier {
function getMultiplyingFactor(address user) external view returns (uint256);
function validUntil(address user) external view returns (uint256);
}

// INFTMultiplier.sol
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./IMultiplier.sol";

interface INFTMultiplier is IMultiplier {
function NFTAddress() external view returns (IERC721);
function hasNFT(address user) external view returns (bool);
}

// PermanentNFTMultiplier.sol
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./INFTMultiplier.sol";

contract PermanentNFTMultiplier is INFTMultiplier {
IERC721 public immutable NFTAddress;
uint256 public immutable multiplyingFactor;
uint256 public constant validity = type(uint256).max;

constructor(IERC721 _nftAddress, uint256 _multiplyingFactor) {
NFTAddress = _nftAddress;
multiplyingFactor = _multiplyingFactor;
}

function getMultiplyingFactor(address user) external view override returns (uint256) {
return hasNFT(user) ? multiplyingFactor : 0;
}

function validUntil(address) external pure override returns (uint256) {
return validity;
}

function hasNFT(address user) public view override returns (bool) {
return NFTAddress.balanceOf(user) > 0;
}
}

// DynamicNFTMultiplier.sol
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "./INFTMultiplier.sol";

contract DynamicNFTMultiplier is INFTMultiplier {
IERC721 public immutable NFTAddress;
mapping(address => uint256) public userToFactor;
mapping(address => uint256) public userToValidity;

constructor(IERC721 _nftAddress) {
NFTAddress = _nftAddress;
}

function getMultiplyingFactor(address user) external view override returns (uint256) {
return hasNFT(user) ? userToFactor[user] : 0;
}

function validUntil(address user) external view override returns (uint256) {
return userToValidity[user];
}

function hasNFT(address user) public view override returns (bool) {
return NFTAddress.balanceOf(user) > 0;
}

function setUserFactor(address user, uint256 factor, uint256 validity) external {
// Add appropriate access control
require(hasNFT(user), "User does not have the required NFT");
userToFactor[user] = factor;
userToValidity[user] = validity;
}
}

0 comments on commit 47bdbff

Please sign in to comment.