diff --git a/script/deploy/DeployNFTMultiplier.s.sol b/script/deploy/DeployNFTMultiplier.s.sol new file mode 100644 index 0000000..08c597c --- /dev/null +++ b/script/deploy/DeployNFTMultiplier.s.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {Script} from "forge-std/Script.sol"; +import {NFTMultiplier} from "src/multipliers/NFTMultiplier.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {console} from "forge-std/console.sol"; + +contract DeployNFTMultiplier is Script { + function run() external { + uint256 deployerPrivateKey; + address nftContractAddress; + uint256 initialMultiplyingFactor; + uint256 validUntilBlock; + + string memory configPath = "deploy_config.json"; + string memory jsonData; + // Try to read the JSON file, if it doesn't exist or can't be read, catch the error + try vm.readFile(configPath) returns (string memory data) { + jsonData = data; + } catch { + console.log("Config file not found or couldn't be read. Falling back to environment variables."); + jsonData = ""; + } + + if (bytes(jsonData).length > 0) { + // Read from JSON if file exists + deployerPrivateKey = vm.parseJsonUint(jsonData, ".deployerPrivateKey"); + nftContractAddress = vm.parseJsonAddress(jsonData, ".nftContractAddress"); + initialMultiplyingFactor = vm.parseJsonUint(jsonData, ".initialMultiplyingFactor"); + validUntilBlock = vm.parseJsonUint(jsonData, ".validUntilBlock"); + } else { + // Fall back to environment variables + deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + nftContractAddress = vm.envAddress("NFT_CONTRACT_ADDRESS"); + initialMultiplyingFactor = vm.envUint("INITIAL_MULTIPLYING_FACTOR"); + validUntilBlock = vm.envUint("VALID_UNTIL_BLOCK"); + } + + // Check if all required variables are set + require(deployerPrivateKey != 0, "Deployer private key not set"); + require(nftContractAddress != address(0), "NFT contract address not set"); + require(initialMultiplyingFactor != 0, "Initial multiplying factor not set"); + require(validUntilBlock != 0, "Valid until block not set"); + + vm.startBroadcast(deployerPrivateKey); + + NFTMultiplier implementation = new NFTMultiplier(); + + bytes memory initData = abi.encodeWithSelector( + NFTMultiplier.initialize.selector, IERC721(nftContractAddress), initialMultiplyingFactor, validUntilBlock + ); + + TransparentUpgradeableProxy proxy = + new TransparentUpgradeableProxy(address(implementation), vm.addr(deployerPrivateKey), initData); + + NFTMultiplier nftMultiplier = NFTMultiplier(address(proxy)); + + vm.stopBroadcast(); + + console.log("NFTMultiplier deployed at:", address(nftMultiplier)); + } +} diff --git a/script/deploy/config/deployNFTMul.json b/script/deploy/config/deployNFTMul.json new file mode 100644 index 0000000..bb6699f --- /dev/null +++ b/script/deploy/config/deployNFTMul.json @@ -0,0 +1,6 @@ +{ + "deployerPrivateKey": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "nftContractAddress": "0x1234567890123456789012345678901234567890", + "initialMultiplyingFactor": 100, + "validUntilBlock": 1000000 +} \ No newline at end of file diff --git a/src/VotingMultipliers.sol b/src/VotingMultipliers.sol new file mode 100644 index 0000000..1d6144d --- /dev/null +++ b/src/VotingMultipliers.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IVotingMultipliers, IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +/// @title VotingMultipliers +/// @notice A contract for managing voting multipliers +/// @dev Implements IVotingMultipliers interface +contract VotingMultipliers is OwnableUpgradeable, IVotingMultipliers { + /// @notice Array of allowlisted multiplier contracts + IMultiplier[] public allowlistedMultipliers; + + /// @notice Calculates the total multiplier for a given _user + /// @param _user The address of the _user + /// @return The total multiplier value for the _user + function getTotalMultipliers(address _user) public view returns (uint256) { + uint256 _totalMultiplier = 0; + for (uint256 i = 0; i < allowlistedMultipliers.length; i++) { + IMultiplier multiplier = allowlistedMultipliers[i]; + if (block.number <= multiplier.validUntil(_user)) { + _totalMultiplier += multiplier.getMultiplyingFactor(_user); + } + } + return _totalMultiplier; + } + + /// @notice Adds a multiplier to the allowlist + /// @param _multiplier The multiplier contract to be added + function addMultiplier(IMultiplier _multiplier) external onlyOwner { + // Check if the multiplier is already allowlisted + for (uint256 i = 0; i < allowlistedMultipliers.length; i++) { + if (allowlistedMultipliers[i] == _multiplier) { + revert MultiplierAlreadyAllowlisted(); + } + } + allowlistedMultipliers.push(_multiplier); + emit MultiplierAdded(_multiplier); + } + + /// @notice Removes a multiplier from the allowlist + /// @param _multiplier The multiplier contract to be removed + function removeMultiplier(IMultiplier _multiplier) external onlyOwner { + bool isallowlisted = false; + for (uint256 i = 0; i < allowlistedMultipliers.length; i++) { + if (allowlistedMultipliers[i] == _multiplier) { + allowlistedMultipliers[i] = allowlistedMultipliers[allowlistedMultipliers.length - 1]; + allowlistedMultipliers.pop(); + isallowlisted = true; + emit MultiplierRemoved(_multiplier); + break; + } + } + if (!isallowlisted) { + revert MultiplierNotAllowlisted(); + } + } + + /// @notice Gets the indexes of valid multipliers for a user + /// @param _user The address of the user + /// @return uint256[] Array of valid multiplier indexes + function getValidMultiplierIndexes(address _user) public view returns (uint256[] memory) { + uint256[] memory validIndexes = new uint256[](allowlistedMultipliers.length); + uint256 count = 0; + + for (uint256 i = 0; i < allowlistedMultipliers.length; i++) { + if ( + block.number <= allowlistedMultipliers[i].validUntil(_user) + && allowlistedMultipliers[i].getMultiplyingFactor(_user) > 0 + ) { + validIndexes[count] = i; + count++; + } + } + + // Create correctly sized array + uint256[] memory result = new uint256[](count); + for (uint256 i = 0; i < count; i++) { + result[i] = validIndexes[i]; + } + return result; + } + + /// @notice Calculates the total multiplier for a given user using specific multiplier indexes + /// @param _user The address of the user + /// @param _multiplierIndexes Array of multiplier indexes to use + /// @return The total multiplier value for the user + function getTotalMultipliers(address _user, uint256[] calldata _multiplierIndexes) public view returns (uint256) { + uint256 _totalMultiplier = 0; + + for (uint256 i = 0; i < _multiplierIndexes.length; i++) { + uint256 index = _multiplierIndexes[i]; + if (index >= allowlistedMultipliers.length) { + revert InvalidMultiplierIndex(); + } + + IMultiplier multiplier = allowlistedMultipliers[index]; + if (block.number <= multiplier.validUntil(_user)) { + _totalMultiplier += multiplier.getMultiplyingFactor(_user); + } + } + return _totalMultiplier; + } +} diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index 40daa6d..9063eff 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -9,18 +9,20 @@ 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 * @notice Distribute $BREAD yield to eligible member projects based on a voted distribution * @author Breadchain Collective - * @custom:coauthor @RonTuretzky + * @custom:coauthor postcapitalistcrypto.eth * @custom:coauthor bagelface.eth * @custom:coauthor prosalads.eth * @custom:coauthor kassandra.eth * @custom:coauthor theblockchainsocialist.eth + * @custom:coauthor github.com/daopunk */ -contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { +contract YieldDistributor is IYieldDistributor, OwnableUpgradeable, VotingMultipliers { /// @notice The address of the $BREAD token contract Bread public BREAD; /// @notice The precision to use for calculations @@ -110,8 +112,11 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { * @return uint256 The voting power of the user */ function getCurrentVotingPower(address _account) public view returns (uint256) { - return this.getVotingPowerForPeriod(BREAD, previousCycleStartingBlock, lastClaimedBlockNumber, _account) - + this.getVotingPowerForPeriod(BUTTERED_BREAD, previousCycleStartingBlock, lastClaimedBlockNumber, _account); + uint256 lastCycleStart = lastClaimedBlockNumber - cycleLength; + uint256 breadVotingPower = getVotingPowerForPeriod(BREAD, lastCycleStart, lastClaimedBlockNumber, _account); + uint256 butteredBreadVotingPower = + getVotingPowerForPeriod(BUTTERED_BREAD, lastCycleStart, lastClaimedBlockNumber, _account); + return breadVotingPower + butteredBreadVotingPower; } /// @notice Get the current accumulated voting power for a user @@ -119,8 +124,8 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { /// @param _account Address of the user to get the current accumulated voting power for /// @return uint256 The current accumulated voting power for the user function getCurrentAccumulatedVotingPower(address _account) public view returns (uint256) { - return this.getVotingPowerForPeriod(BUTTERED_BREAD, lastClaimedBlockNumber, block.number, _account) - + this.getVotingPowerForPeriod(BREAD, lastClaimedBlockNumber, block.number, _account); + return getVotingPowerForPeriod(BUTTERED_BREAD, lastClaimedBlockNumber, block.number, _account) + + getVotingPowerForPeriod(BREAD, lastClaimedBlockNumber, block.number, _account); } /** @@ -135,7 +140,7 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { uint256 _start, uint256 _end, address _account - ) external view returns (uint256) { + ) public view returns (uint256) { if (_start >= _end) revert StartMustBeBeforeEnd(); if (_end > block.number) revert EndAfterCurrentBlock(); @@ -227,6 +232,19 @@ contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { _castVote(msg.sender, _points, _currentVotingPower); } + /** + * @notice Cast votes for the distribution of $BREAD yield with multipliers + * @param _points List of points as integers for each project + * @param _multiplierIndices List of indices of multipliers to use for each project + */ + function castVoteWithMultipliers(uint256[] calldata _points, uint256[] calldata _multiplierIndices) public { + uint256 _currentVotingPower = getCurrentVotingPower(msg.sender); + if (_currentVotingPower < minRequiredVotingPower) revert BelowMinRequiredVotingPower(); + uint256 multiplier = getTotalMultipliers(msg.sender, _multiplierIndices); + _currentVotingPower = multiplier == 0 ? _currentVotingPower : (_currentVotingPower * multiplier) / PRECISION; + _castVote(msg.sender, _points, _currentVotingPower); + } + /** * @notice Internal function for casting votes for a specified user * @param _account Address of user to cast votes for diff --git a/src/interfaces/IVotingMultipliers.sol b/src/interfaces/IVotingMultipliers.sol new file mode 100644 index 0000000..b4f3335 --- /dev/null +++ b/src/interfaces/IVotingMultipliers.sol @@ -0,0 +1,37 @@ +pragma solidity ^0.8.22; + +import {IMultiplier} from "./multipliers/IMultiplier.sol"; + +/// @title IVotingMultipliers +/// @notice Interface for the VotingMultipliers contract +/// @dev This interface defines the structure and functions for managing voting multipliers +interface IVotingMultipliers { + /// @notice Thrown when attempting to add a multiplier that is already allowlisted + error MultiplierAlreadyAllowlisted(); + /// @notice Thrown when attempting to remove a multiplier that is not allowlisted + error MultiplierNotAllowlisted(); + /// @notice Thrown when an invalid multiplier index is provided + error InvalidMultiplierIndex(); + /// @notice Emitted when a new multiplier is added to the allowlist + /// @param multiplier The address of the added multiplier + + event MultiplierAdded(IMultiplier indexed multiplier); + /// @notice Emitted when a multiplier is removed from the allowlist + /// @param multiplier The address of the removed multiplier + event MultiplierRemoved(IMultiplier indexed multiplier); + /// @notice Returns the multiplier at the specified index in the allowlist + /// @param index The index of the multiplier in the allowlist + /// @return The multiplier contract at the specified index + + function allowlistedMultipliers(uint256 index) external view returns (IMultiplier); + /// @notice Calculates the total multiplier for a given _user + /// @param __user The address of the _user + /// @return The total multiplier value for the _user + function getTotalMultipliers(address __user) external view returns (uint256); + /// @notice Adds a multiplier to the allowlist + /// @param _multiplier The multiplier contract to be added + function addMultiplier(IMultiplier _multiplier) external; + /// @notice Removes a multiplier from the allowlist + /// @param _multiplier The multiplier contract to be removed + function removeMultiplier(IMultiplier _multiplier) external; +} diff --git a/src/interfaces/multipliers/ICrossChainProveableMultiplier.sol b/src/interfaces/multipliers/ICrossChainProveableMultiplier.sol new file mode 100644 index 0000000..850f1de --- /dev/null +++ b/src/interfaces/multipliers/ICrossChainProveableMultiplier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; + +/// @title Cross-Chain Proveable Multiplier Interface +/// @notice Interface for contracts that provide a cross-chain proveable multiplying factor + +interface ICrossChainProveableMultiplier is IProveableMultiplier { + /// @notice Get the address of the bridge contract + /// @return The address of the contract used for cross-chain communication + function bridge() external view returns (address); +} diff --git a/src/interfaces/multipliers/IDynamicNFTMultiplier.sol b/src/interfaces/multipliers/IDynamicNFTMultiplier.sol new file mode 100644 index 0000000..8a603a5 --- /dev/null +++ b/src/interfaces/multipliers/IDynamicNFTMultiplier.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; +/// @title Dynamic NFT Multiplier Interface +/// @notice Interface for contracts that provide a dynamic multiplying factor for _users based on NFT ownership +/// @dev Extends the INFTMultiplier interface with dynamic multiplier functionality + +interface IDynamicNFTMultiplier is INFTMultiplier { + /// @notice Get the multiplying factor for a _user + /// @param _user The address of the _user + /// @return The multiplying factor for the _user + function _userToFactor(address _user) external view returns (uint256); + + /// @notice Get the validity period for a _user's factor + /// @param _user The address of the _user + /// @return The timestamp until which the _user's factor is valid + function _userToValidity(address _user) external view returns (uint256); +} diff --git a/src/interfaces/multipliers/IMultiplier.sol b/src/interfaces/multipliers/IMultiplier.sol new file mode 100644 index 0000000..9f3a835 --- /dev/null +++ b/src/interfaces/multipliers/IMultiplier.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IMultiplier { + /// @notice Returns the voting multiplier for `_user`. + function getMultiplyingFactor(address _user) external view returns (uint256); + + /// @notice Returns the validity period of the multiplier for `_user`. + function validUntil(address _user) external view returns (uint256); +} diff --git a/src/interfaces/multipliers/INFTMultiplier.sol b/src/interfaces/multipliers/INFTMultiplier.sol new file mode 100644 index 0000000..964eb46 --- /dev/null +++ b/src/interfaces/multipliers/INFTMultiplier.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IMultiplier} from "src/interfaces/multipliers/IMultiplier.sol"; + +/// @title NFT Multiplier Interface +/// @notice Interface for contracts that provide multiplying factors based on NFT ownership +/// @dev Extends the IMultiplier interface with NFT-specific functionality +interface INFTMultiplier is IMultiplier { + /// @notice Get the address of the NFT contract + /// @return The address of the NFT contract used for checking ownership + function NFT_ADDRESS() external view returns (IERC721); + + /// @notice Check if a _user owns an NFT + /// @param _user The address of the _user to check + /// @return True if the _user owns at least one NFT, false otherwise + function hasNFT(address _user) external view returns (bool); +} diff --git a/src/interfaces/multipliers/IOffChainProveableMultiplier.sol b/src/interfaces/multipliers/IOffChainProveableMultiplier.sol new file mode 100644 index 0000000..b19e77f --- /dev/null +++ b/src/interfaces/multipliers/IOffChainProveableMultiplier.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; + +/// @title Off-Chain Proveable Multiplier Interface +/// @notice Interface for contracts that provide an off-chain proveable multiplying factor +interface IOffChainProveableMultiplier is IProveableMultiplier { + /// @notice Get the address of the pull oracle + /// @return The address of the oracle used for off-chain data verification + function oracle() external view returns (address); +} diff --git a/src/interfaces/multipliers/IOnChainProveableMultiplier.sol b/src/interfaces/multipliers/IOnChainProveableMultiplier.sol new file mode 100644 index 0000000..442522c --- /dev/null +++ b/src/interfaces/multipliers/IOnChainProveableMultiplier.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IProveableMultiplier} from "src/interfaces/multipliers/IProveableMultiplier.sol"; +/// @title On-Chain Proveable Multiplier Interface +/// @notice Interface for contracts that provide an on-chain proveable multiplying factor + +interface IOnChainProveableMultiplier is IProveableMultiplier { + /// @notice Get the address of the activity contract + /// @return The address of the contract used for verifying on-chain activities + function activityContract() external view returns (address); +} diff --git a/src/interfaces/multipliers/IProveableMultiplier.sol b/src/interfaces/multipliers/IProveableMultiplier.sol new file mode 100644 index 0000000..0f08f35 --- /dev/null +++ b/src/interfaces/multipliers/IProveableMultiplier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {IDynamicNFTMultiplier} from "src/interfaces/multipliers/IDynamicNFTMultiplier.sol"; +/// @title Proveable Multiplier Interface +/// @notice Interface for contracts that provide a proveable multiplying factor based on _user activities + +interface IProveableMultiplier is IERC721, IDynamicNFTMultiplier { + /// @notice Submit activities to potentially earn or upgrade an NFT + /// @param data Encoded data representing the activities + function submitActivities(bytes calldata data) external; +} diff --git a/src/multipliers/NFTMultiplier.sol b/src/multipliers/NFTMultiplier.sol new file mode 100644 index 0000000..9ede0c0 --- /dev/null +++ b/src/multipliers/NFTMultiplier.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/// @title NFT Multiplier +/// @notice Implementation of INFTMultiplier interface +/// @dev Provides multiplying factors based on NFT ownership +contract NFTMultiplier is INFTMultiplier, Initializable, OwnableUpgradeable { + IERC721 public nftContract; + uint256 public multiplyingFactor; + uint256 public validUntilBlock; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /// @notice Initializer function to set the NFT contract address and initial multiplying factor + /// @param _nftContract Address of the NFT contract + /// @param _initialMultiplyingFactor Initial multiplying factor + /// @param _validUntilBlock Block number until which the multiplier is valid + function initialize(IERC721 _nftContract, uint256 _initialMultiplyingFactor, uint256 _validUntilBlock) + public + initializer + { + __Ownable_init(msg.sender); + + nftContract = _nftContract; + multiplyingFactor = _initialMultiplyingFactor; + validUntilBlock = _validUntilBlock; + } + + /// @notice Get the address of the NFT contract + /// @return The address of the NFT contract used for checking ownership + function NFT_ADDRESS() external view override returns (IERC721) { + return nftContract; + } + + /// @notice Check if a _user owns an NFT + /// @param _user The address of the _user to check + /// @return True if the _user owns at least one NFT, false otherwise + function hasNFT(address _user) public view override returns (bool) { + return nftContract.balanceOf(_user) > 0; + } + + /// @notice Get the multiplying factor for a given _user + /// @param _user The address of the _user + /// @return The multiplying factor if the _user owns an NFT, 0 otherwise + function getMultiplyingFactor(address _user) external view override returns (uint256) { + return hasNFT(_user) ? multiplyingFactor : 0; + } + + /// @notice Get the block number until which the multiplier is valid + /// @return The block number until which the multiplier is valid + function validUntil(address /* _user */ ) external view override returns (uint256) { + return validUntilBlock; + } + + /// @notice Update the multiplying factor + /// @param _newMultiplyingFactor New multiplying factor (in basis points) + function updateMultiplyingFactor(uint256 _newMultiplyingFactor) external onlyOwner { + multiplyingFactor = _newMultiplyingFactor; + } + + /// @notice Update the valid until block + /// @param _newValidUntilBlock New block number until which the multiplier is valid + function updateValidUntilBlock(uint256 _newValidUntilBlock) external onlyOwner { + validUntilBlock = _newValidUntilBlock; + } + + /// @notice Update the NFT contract address + /// @param _newNFTContract New NFT contract address + function updateNFTContract(IERC721 _newNFTContract) external onlyOwner { + nftContract = _newNFTContract; + } +} diff --git a/src/multipliers/PermanentNFTMultiplier.sol b/src/multipliers/PermanentNFTMultiplier.sol new file mode 100644 index 0000000..5e86a42 --- /dev/null +++ b/src/multipliers/PermanentNFTMultiplier.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; +import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; + +/// @title Permanent NFT Multiplier +/// @notice This contract provides a permanent multiplying factor for _users based on NFT ownership +/// @dev Implements the INFTMultiplier interface +contract PermanentNFTMultiplier is INFTMultiplier { + /// @notice The address of the NFT contract + IERC721 public immutable NFT_ADDRESS; + /// @notice The multiplying factor applied to NFT holders + uint256 public immutable FACTOR; + + /// @notice Constructs the PermanentNFTMultiplier contract + /// @param _NFT_ADDRESS The address of the NFT contract to check for ownership + /// @param _FACTOR The multiplying factor to apply to NFT holders + constructor(IERC721 _NFT_ADDRESS, uint256 _FACTOR) { + NFT_ADDRESS = _NFT_ADDRESS; + FACTOR = _FACTOR; + } + + /// @notice Get the multiplying factor for a _user + /// @param _user The address of the _user + /// @return The multiplying factor if the _user has an NFT, 0 otherwise + function getMultiplyingFactor(address _user) external view override returns (uint256) { + return hasNFT(_user) ? FACTOR : 0; + } + + /// @notice Get the validity period for a _user's factor + /// @return Always returns type(uint256).max as the factor is permanent + function validUntil(address /* _user */ ) external pure override returns (uint256) { + return type(uint256).max; + } + + /// @notice Check if a _user owns an NFT + /// @param _user The address of the _user to check + /// @return True if the _user owns at least one NFT, false otherwise + function hasNFT(address _user) public view override returns (bool) { + return NFT_ADDRESS.balanceOf(_user) > 0; + } +} diff --git a/src/test/MockMultiplier.sol b/src/test/MockMultiplier.sol new file mode 100644 index 0000000..789c621 --- /dev/null +++ b/src/test/MockMultiplier.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +import {IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; + +/// @title MockMultiplier +/// @notice A mock contract implementing the IMultiplier interface for testing purposes +contract MockMultiplier is IMultiplier { + uint256 private _multiplyingFactor; + uint256 private _validUntil; + + /// @notice Sets the multiplying factor and valid until block for testing + /// @param _factor The multiplying factor to set + /// @param _validUntilBlock The block number until which the multiplier is valid + function setMultiplier(uint256 _factor , uint256 _validUntilBlock) external { + _multiplyingFactor = _factor ; + _validUntil = _validUntilBlock; + } + + /// @notice Returns the multiplying factor for a given _user + /// @return The multiplying factor + function getMultiplyingFactor(address /* _user */ ) external view returns (uint256) { + return _multiplyingFactor; + } + + /// @notice Returns the block number until which the multiplier is valid for a given _user + /// @return The block number until which the multiplier is valid + function validUntil(address /* _user */ ) external view returns (uint256) { + return _validUntil; + } +} diff --git a/test/YieldDistributor.t.sol b/test/YieldDistributor.t.sol index 32caac9..83c2fb9 100644 --- a/test/YieldDistributor.t.sol +++ b/test/YieldDistributor.t.sol @@ -9,11 +9,15 @@ import {ERC20VotesUpgradeable} from import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; import {TransparentUpgradeableProxy} from "openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; - import {YieldDistributor, IYieldDistributor} from "src/YieldDistributor.sol"; import {YieldDistributorTestWrapper} from "src/test/YieldDistributorTestWrapper.sol"; - import {ButteredBread} from "src/ButteredBread.sol"; +import {VotingMultipliers, IVotingMultipliers} from "src/VotingMultipliers.sol"; +import {MockMultiplier} from "src/test/MockMultiplier.sol"; +import {IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; +import {NFTMultiplier} from "src/multipliers/NFTMultiplier.sol"; +import {DeployNFTMultiplier} from "script/deploy/DeployNFTMultiplier.s.sol"; +import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; abstract contract Bread is ERC20VotesUpgradeable, OwnableUpgradeable { function claimYield(uint256 amount, address receiver) public virtual; @@ -55,7 +59,7 @@ contract YieldDistributorTest is Test { uint256 _minRequiredVotingPower = stdJson.readUint(config_data, "._minRequiredVotingPower"); - function setUp() public { + function setUp() public virtual { vm.createSelectFork(vm.rpcUrl("gnosis")); YieldDistributorTestWrapper yieldDistributorImplementation = new YieldDistributorTestWrapper(); @@ -177,7 +181,7 @@ contract YieldDistributorTest is Test { uint256 votedSplit = yieldAccrued - fixedSplit; uint256 projectsLength = yieldDistributor2.getProjectsLength(); // Getting the balance of the project after the distribution and checking if it similiar to the yield accrued (there may be rounding issues) - uint256 bread_bal_after = bread.balanceOf(address(secondProject)); + uint256 bread_bal_after = bread.balanceOf(secondProject); assertGt(bread_bal_after, ((fixedSplit + votedSplit) / projectsLength) - marginOfError); } @@ -446,3 +450,259 @@ contract YieldDistributorTest is Test { yieldDistributor.castVote(percentages); } } + +contract VotingMultipliersTest is YieldDistributorTest { + MockMultiplier public mockMultiplier1; + MockMultiplier public mockMultiplier2; + NFTMultiplier public nftMultiplier; + + function setUp() public override { + super.setUp(); + + mockMultiplier1 = new MockMultiplier(); + mockMultiplier2 = new MockMultiplier(); + } + + function testAddMultiplier() public { + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier1))); + assertEq(address(yieldDistributor.allowlistedMultipliers(0)), address(mockMultiplier1)); + } + + function testAddMultiplierRevertAlreadyAllowlisted() public { + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier1))); + vm.expectRevert(IVotingMultipliers.MultiplierAlreadyAllowlisted.selector); + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier1))); + } + + function testRemoveMultiplier() public { + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier1))); + yieldDistributor.removeMultiplier(IMultiplier(address(mockMultiplier1))); + vm.expectRevert(); + yieldDistributor.allowlistedMultipliers(0); + } + + function testRemoveMultiplierRevertNotallowlisted() public { + vm.expectRevert(IVotingMultipliers.MultiplierNotAllowlisted.selector); + yieldDistributor.removeMultiplier(IMultiplier(address(mockMultiplier1))); + } + + function testGetTotalMultipliers() public { + uint256 factor1 = 1.5e18; + uint256 factor2 = 2e18; + uint256 validUntil = block.number + 1000; + mockMultiplier1.setMultiplier(factor1, validUntil); + mockMultiplier2.setMultiplier(factor2, validUntil); + + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier1))); + yieldDistributor.addMultiplier(IMultiplier(address(mockMultiplier2))); + + uint256 totalMultiplier = yieldDistributor.getTotalMultipliers(address(this)); + assertEq(totalMultiplier, factor1 + factor2); + } + + function testCastVoteWithMultipliersIndices() public { + // Set up two multipliers with different factors + mockMultiplier1.setMultiplier(1.5e18, type(uint256).max); + mockMultiplier2.setMultiplier(2e18, type(uint256).max); + + address voter = address(0x1); + address[] memory voters = new address[](1); + voters[0] = voter; + + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + uint256 initialVotingPower = yieldDistributor.getCurrentVotingPower(voter); + + // Add multipliers to the distributor + yieldDistributor.addMultiplier(mockMultiplier1); + yieldDistributor.addMultiplier(mockMultiplier2); + + // Set up vote points and multiplier indices + uint256[] memory points = new uint256[](1); + points[0] = 100; + uint256[] memory multiplierIndices = new uint256[](2); + multiplierIndices[0] = 0; // mockMultiplier1 + multiplierIndices[1] = 1; // mockMultiplier2 + + vm.startPrank(voter); + yieldDistributor.castVoteWithMultipliers(points, multiplierIndices); + + // Expected voting power = initial * (1.5 + 2.0) + uint256 expectedVotingPower = (initialVotingPower * 3.5e18) / yieldDistributor.PRECISION(); + assertEq(yieldDistributor.projectDistributions(0), expectedVotingPower); + vm.stopPrank(); + } + + function testCastVoteWithMultipliersIndicesInvalidIndex() public { + mockMultiplier1.setMultiplier(1.5e18, type(uint256).max); + + address voter = address(0x1); + address[] memory voters = new address[](1); + voters[0] = voter; + + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + yieldDistributor.addMultiplier(mockMultiplier1); + + uint256[] memory points = new uint256[](1); + points[0] = 100; + uint256[] memory multiplierIndices = new uint256[](1); + multiplierIndices[0] = 999; // Invalid index + + vm.startPrank(voter); + vm.expectRevert(); + yieldDistributor.castVoteWithMultipliers(points, multiplierIndices); + vm.stopPrank(); + } + + function testCastVoteWithMultipliersExpiredMultiplier() public { + // Set up one expired and one valid multiplier + mockMultiplier1.setMultiplier(1.5e18, block.number - 1); // Expired + mockMultiplier2.setMultiplier(2e18, type(uint256).max); // Valid + + address voter = address(0x1); + address[] memory voters = new address[](1); + voters[0] = voter; + + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + uint256 initialVotingPower = yieldDistributor.getCurrentVotingPower(voter); + + yieldDistributor.addMultiplier(mockMultiplier1); + yieldDistributor.addMultiplier(mockMultiplier2); + + uint256[] memory points = new uint256[](1); + points[0] = 100; + uint256[] memory multiplierIndices = new uint256[](2); + multiplierIndices[0] = 0; + multiplierIndices[1] = 1; + + vm.startPrank(voter); + yieldDistributor.castVoteWithMultipliers(points, multiplierIndices); + + // Only the valid multiplier should be applied + uint256 expectedVotingPower = (initialVotingPower * 2e18) / yieldDistributor.PRECISION(); + assertEq(yieldDistributor.projectDistributions(0), expectedVotingPower); + vm.stopPrank(); + } + + function testFuzzCastVoteWithMultipliersIndices( + uint256 multiplier1Factor, + uint256 multiplier2Factor, + uint8 numIndices + ) public { + // Bound the inputs to reasonable ranges + multiplier1Factor = bound(multiplier1Factor, 1e18, 5e18); + multiplier2Factor = bound(multiplier2Factor, 1e18, 5e18); + numIndices = uint8(bound(numIndices, 1, 2)); + + mockMultiplier1.setMultiplier(multiplier1Factor, type(uint256).max); + mockMultiplier2.setMultiplier(multiplier2Factor, type(uint256).max); + + address voter = address(0x1); + address[] memory voters = new address[](1); + voters[0] = voter; + + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + uint256 initialVotingPower = yieldDistributor.getCurrentVotingPower(voter); + + yieldDistributor.addMultiplier(mockMultiplier1); + yieldDistributor.addMultiplier(mockMultiplier2); + + uint256[] memory points = new uint256[](1); + points[0] = 100; + uint256[] memory multiplierIndices = new uint256[](numIndices); + for (uint8 i = 0; i < numIndices; i++) { + multiplierIndices[i] = i; + } + + vm.prank(voter); + yieldDistributor.castVoteWithMultipliers(points, multiplierIndices); + + // Calculate expected total multiplier based on number of indices + uint256 totalMultiplier = numIndices == 1 ? multiplier1Factor : (multiplier1Factor + multiplier2Factor); + uint256 expectedVotingPower = (initialVotingPower * totalMultiplier) / yieldDistributor.PRECISION(); + + assertApproxEqRel(yieldDistributor.projectDistributions(0), expectedVotingPower, 1e15); // Allow 0.1% deviation + } + + function testFuzzCastVoteWithDynamicMultipliers(uint8 numMultipliers, bytes32[] calldata multiplierSeeds) public { + // Bound number of multipliers to reasonable range (1-10) + numMultipliers = uint8(bound(numMultipliers, 1, 10)); + + // Create array of mock multipliers + MockMultiplier[] memory multipliers = new MockMultiplier[](numMultipliers); + uint256 expectedTotalMultiplier = 0; + uint256 numValidMultipliers = 0; + // Set up each multiplier with unique factor based on seed + for (uint8 i = 0; i < numMultipliers; i++) { + multipliers[i] = new MockMultiplier(); + + // Generate multiplier factor from seed (between 1x and 5x) + uint256 multiplierFactor; + if (i < multiplierSeeds.length) { + multiplierFactor = bound(uint256(multiplierSeeds[i]), 1e18, 5e18); + } else { + multiplierFactor = bound(uint256(keccak256(abi.encode(i))), 1e18, 5e18); + } + + // Randomly decide if multiplier should be expired (10% chance) + bool isExpired = uint256(keccak256(abi.encode(multiplierFactor, i))) % 10 == 0; + uint256 validUntil = isExpired ? block.number - 1 : type(uint256).max; + + multipliers[i].setMultiplier(multiplierFactor, validUntil); + yieldDistributor.addMultiplier(multipliers[i]); + + if (!isExpired) { + expectedTotalMultiplier += multiplierFactor; + numValidMultipliers++; + } + } + + // Set up voter + address voter = address(0x1); + address[] memory voters = new address[](1); + voters[0] = voter; + + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + uint256 initialVotingPower = yieldDistributor.getCurrentVotingPower(voter); + + // Create vote points and multiplier indices + uint256[] memory points = new uint256[](1); + points[0] = 100; + uint256[] memory multiplierIndices = new uint256[](numMultipliers); + for (uint8 i = 0; i < numMultipliers; i++) { + multiplierIndices[i] = i; + } + uint256[] memory fetchedMultipliers = yieldDistributor.getValidMultiplierIndexes(voter); + assertEq(fetchedMultipliers.length, numValidMultipliers); + vm.prank(voter); + yieldDistributor.castVoteWithMultipliers(points, fetchedMultipliers); + + // If no valid multipliers, should use precision as multiplier + if (expectedTotalMultiplier == 0) { + expectedTotalMultiplier = yieldDistributor.PRECISION(); + } + + uint256 expectedVotingPower = (initialVotingPower * expectedTotalMultiplier) / yieldDistributor.PRECISION(); + + // Allow for small rounding errors in calculation + assertApproxEqRel( + yieldDistributor.projectDistributions(0), + expectedVotingPower, + 1e15 // 0.1% tolerance + ); + + // Verify all multipliers were properly registered + for (uint8 i = 0; i < numMultipliers; i++) { + assertEq(address(yieldDistributor.allowlistedMultipliers(i)), address(multipliers[i])); + } + } +} diff --git a/test/upgrades/v1.0.2/YieldDistributor.sol b/test/upgrades/v1.0.2/YieldDistributor.sol new file mode 100644 index 0000000..648ce7a --- /dev/null +++ b/test/upgrades/v1.0.2/YieldDistributor.sol @@ -0,0 +1,5314 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20 ^0.8.22; + +// lib/bread-token-v2/src/interfaces/IBread.sol + +interface IBread { + function claimYield(uint256 amount, address receiver) external; + function yieldAccrued() external view returns (uint256); + function setYieldClaimer(address _yieldClaimer) external; +} + +// lib/bread-token-v2/src/interfaces/ISXDAI.sol + +interface ISXDAI { + function deposit(uint256 assets, address receiver) external returns (uint256); + function withdraw(uint256 assets, address receiver, address owner) external returns (uint256); + + function convertToAssets(uint256 shares) external view returns (uint256); +} + +// lib/bread-token-v2/src/interfaces/IWXDAI.sol + +interface IWXDAI { + function deposit() external payable; + function withdraw(uint256) external; +} + +// lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) + +/** + * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed + * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an + * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer + * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. + * + * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be + * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in + * case an upgrade adds a module that needs to be initialized. + * + * For example: + * + * [.hljs-theme-light.nopadding] + * ```solidity + * contract MyToken is ERC20Upgradeable { + * function initialize() initializer public { + * __ERC20_init("MyToken", "MTK"); + * } + * } + * + * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { + * function initializeV2() reinitializer(2) public { + * __ERC20Permit_init("MyToken"); + * } + * } + * ``` + * + * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as + * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. + * + * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure + * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. + * + * [CAUTION] + * ==== + * Avoid leaving a contract uninitialized. + * + * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation + * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke + * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: + * + * [.hljs-theme-light.nopadding] + * ``` + * /// @custom:oz-upgrades-unsafe-allow constructor + * constructor() { + * _disableInitializers(); + * } + * ``` + * ==== + */ +abstract contract Initializable { + /** + * @dev Storage of the initializable contract. + * + * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions + * when using with upgradeable contracts. + * + * @custom:storage-location erc7201:openzeppelin.storage.Initializable + */ + struct InitializableStorage { + /** + * @dev Indicates that the contract has been initialized. + */ + uint64 _initialized; + /** + * @dev Indicates that the contract is in the process of being initialized. + */ + bool _initializing; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; + + /** + * @dev The contract is already initialized. + */ + error InvalidInitialization(); + + /** + * @dev The contract is not initializing. + */ + error NotInitializing(); + + /** + * @dev Triggered when the contract has been initialized or reinitialized. + */ + event Initialized(uint64 version); + + /** + * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, + * `onlyInitializing` functions can be used to initialize parent contracts. + * + * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any + * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in + * production. + * + * Emits an {Initialized} event. + */ + modifier initializer() { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + // Cache values to avoid duplicated sloads + bool isTopLevelCall = !$._initializing; + uint64 initialized = $._initialized; + + // Allowed calls: + // - initialSetup: the contract is not in the initializing state and no previous version was + // initialized + // - construction: the contract is initialized at version 1 (no reininitialization) and the + // current contract is just being deployed + bool initialSetup = initialized == 0 && isTopLevelCall; + bool construction = initialized == 1 && address(this).code.length == 0; + + if (!initialSetup && !construction) { + revert InvalidInitialization(); + } + $._initialized = 1; + if (isTopLevelCall) { + $._initializing = true; + } + _; + if (isTopLevelCall) { + $._initializing = false; + emit Initialized(1); + } + } + + /** + * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the + * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be + * used to initialize parent contracts. + * + * A reinitializer may be used after the original initialization step. This is essential to configure modules that + * are added through upgrades and that require initialization. + * + * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` + * cannot be nested. If one is invoked in the context of another, execution will revert. + * + * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in + * a contract, executing them in the right order is up to the developer or operator. + * + * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. + * + * Emits an {Initialized} event. + */ + modifier reinitializer(uint64 version) { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + if ($._initializing || $._initialized >= version) { + revert InvalidInitialization(); + } + $._initialized = version; + $._initializing = true; + _; + $._initializing = false; + emit Initialized(version); + } + + /** + * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the + * {initializer} and {reinitializer} modifiers, directly or indirectly. + */ + modifier onlyInitializing() { + _checkInitializing(); + _; + } + + /** + * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. + */ + function _checkInitializing() internal view virtual { + if (!_isInitializing()) { + revert NotInitializing(); + } + } + + /** + * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. + * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized + * to any version. It is recommended to use this to lock implementation contracts that are designed to be called + * through proxies. + * + * Emits an {Initialized} event the first time it is successfully executed. + */ + function _disableInitializers() internal virtual { + // solhint-disable-next-line var-name-mixedcase + InitializableStorage storage $ = _getInitializableStorage(); + + if ($._initializing) { + revert InvalidInitialization(); + } + if ($._initialized != type(uint64).max) { + $._initialized = type(uint64).max; + emit Initialized(type(uint64).max); + } + } + + /** + * @dev Returns the highest version that has been initialized. See {reinitializer}. + */ + function _getInitializedVersion() internal view returns (uint64) { + return _getInitializableStorage()._initialized; + } + + /** + * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. + */ + function _isInitializing() internal view returns (bool) { + return _getInitializableStorage()._initializing; + } + + /** + * @dev Returns a pointer to the storage namespace. + */ + // solhint-disable-next-line var-name-mixedcase + function _getInitializableStorage() private pure returns (InitializableStorage storage $) { + assembly { + $.slot := INITIALIZABLE_STORAGE + } + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/governance/utils/IVotes.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol) + +/** + * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. + */ +interface IVotes { + /** + * @dev The signature used has expired. + */ + error VotesExpiredSignature(uint256 expiry); + + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); + + /** + * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of voting units. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousVotes, uint256 newVotes); + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) external view returns (uint256); + + /** + * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is + * configured to use block numbers, this will return the value at the end of the corresponding block. + */ + function getPastVotes(address account, uint256 timepoint) external view returns (uint256); + + /** + * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is + * configured to use block numbers, this will return the value at the end of the corresponding block. + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + */ + function getPastTotalSupply(uint256 timepoint) external view returns (uint256); + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) external view returns (address); + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) external; + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external; +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/interfaces/IERC5267.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol) + +interface IERC5267 { + /** + * @dev MAY be emitted to signal that the domain could have changed. + */ + event EIP712DomainChanged(); + + /** + * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 + * signature. + */ + function eip712Domain() + external + view + returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/interfaces/IERC6372.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol) + +interface IERC6372 { + /** + * @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting). + */ + function clock() external view returns (uint48); + + /** + * @dev Description of the clock + */ + // solhint-disable-next-line func-name-mixedcase + function CLOCK_MODE() external view returns (string memory); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) + +/** + * @dev Standard ERC20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); + + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 value) external returns (bool); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + * + * ==== Security Considerations + * + * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature + * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be + * considered as an intention to spend the allowance in any specific way. The second is that because permits have + * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should + * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be + * generally recommended is: + * + * ```solidity + * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { + * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} + * doThing(..., value); + * } + * + * function doThing(..., uint256 value) public { + * token.safeTransferFrom(msg.sender, address(this), value); + * ... + * } + * ``` + * + * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of + * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also + * {SafeERC20-safeTransferFrom}). + * + * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so + * contracts should have entry points that don't rely on permit. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + * + * CAUTION: See Security Considerations above. + */ + function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) + external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Address.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev The ETH balance of the account is not enough to perform the operation. + */ + error AddressInsufficientBalance(address account); + + /** + * @dev There's no code at `target` (it is not a contract). + */ + error AddressEmptyCode(address target); + + /** + * @dev A call to an address target failed. The target may have reverted. + */ + error FailedInnerCall(); + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + if (address(this).balance < amount) { + revert AddressInsufficientBalance(address(this)); + } + + (bool success,) = recipient.call{value: amount}(""); + if (!success) { + revert FailedInnerCall(); + } + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason or custom error, it is bubbled + * up by this function (like regular Solidity function calls). However, if + * the call reverted with no returned reason, this function reverts with a + * {FailedInnerCall} error. + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + if (address(this).balance < value) { + revert AddressInsufficientBalance(address(this)); + } + (bool success, bytes memory returndata) = target.call{value: value}(data); + return verifyCallResultFromTarget(target, success, returndata); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + */ + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResultFromTarget(target, success, returndata); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + */ + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResultFromTarget(target, success, returndata); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target + * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an + * unsuccessful call. + */ + function verifyCallResultFromTarget(address target, bool success, bytes memory returndata) + internal + view + returns (bytes memory) + { + if (!success) { + _revert(returndata); + } else { + // only check if target is a contract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + if (returndata.length == 0 && target.code.length == 0) { + revert AddressEmptyCode(target); + } + return returndata; + } + } + + /** + * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the + * revert reason or with a default {FailedInnerCall} error. + */ + function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { + if (!success) { + _revert(returndata); + } else { + return returndata; + } + } + + /** + * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. + */ + function _revert(bytes memory returndata) private pure { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert FailedInnerCall(); + } + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS + } + + /** + * @dev The signature derives the `address(0)`. + */ + error ECDSAInvalidSignature(); + + /** + * @dev The signature has an invalid length. + */ + error ECDSAInvalidSignatureLength(uint256 length); + + /** + * @dev The signature has an S value that is in the upper half order. + */ + error ECDSAInvalidSignatureS(bytes32 s); + + /** + * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not + * return address(0) without also returning an error description. Errors are documented using an enum (error type) + * and a bytes32 providing additional information about the error. + * + * If no error is returned, then the address can be used for verification purposes. + * + * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else { + return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + */ + function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { + unchecked { + bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + // We do not check for an overflow here since the shift operation results in 0 or 1. + uint8 v = uint8((uint256(vs) >> 255) + 27); + return tryRecover(hash, v, r, s); + } + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + */ + function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) + internal + pure + returns (address, RecoverError, bytes32) + { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS, s); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature, bytes32(0)); + } + + return (signer, RecoverError.NoError, bytes32(0)); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. + */ + function _throwError(RecoverError error, bytes32 errorArg) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert ECDSAInvalidSignature(); + } else if (error == RecoverError.InvalidSignatureLength) { + revert ECDSAInvalidSignatureLength(uint256(errorArg)); + } else if (error == RecoverError.InvalidSignatureS) { + revert ECDSAInvalidSignatureS(errorArg); + } + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/math/Math.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + /** + * @dev Muldiv operation overflow. + */ + error MathOverflowedMulDiv(); + + enum Rounding { + Floor, // Toward negative infinity + Ceil, // Toward positive infinity + Trunc, // Toward zero + Expand // Away from zero + + } + + /** + * @dev Returns the addition of two unsigned integers, with an overflow flag. + */ + function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + uint256 c = a + b; + if (c < a) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the subtraction of two unsigned integers, with an overflow flag. + */ + function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b > a) return (false, 0); + return (true, a - b); + } + } + + /** + * @dev Returns the multiplication of two unsigned integers, with an overflow flag. + */ + function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) return (true, 0); + uint256 c = a * b; + if (c / a != b) return (false, 0); + return (true, c); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a division by zero flag. + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a / b); + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { + unchecked { + if (b == 0) return (false, 0); + return (true, a % b); + } + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds towards infinity instead + * of rounding towards zero. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + if (b == 0) { + // Guarantee the same behavior as in a regular Solidity division. + return a / b; + } + + // (a + b - 1) / b can overflow on addition, so we distribute. + return a == 0 ? 0 : (a - 1) / b + 1; + } + + /** + * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or + * denominator == 0. + * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by + * Uniswap Labs also under MIT license. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2^256 + prod0. + uint256 prod0 = x * y; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division. + if (prod1 == 0) { + // Solidity will revert if denominator == 0, unlike the div opcode on its own. + // The surrounding unchecked block does not change this fact. + // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. + return prod0 / denominator; + } + + // Make sure the result is less than 2^256. Also prevents denominator == 0. + if (denominator <= prod1) { + revert MathOverflowedMulDiv(); + } + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. + // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. + + uint256 twos = denominator & (0 - denominator); + assembly { + // Divide denominator by twos. + denominator := div(denominator, twos) + + // Divide [prod1 prod0] by twos. + prod0 := div(prod0, twos) + + // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. + twos := add(div(sub(0, twos), twos), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * twos; + + // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such + // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2^4. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also + // works in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2^8 + inverse *= 2 - denominator * inverse; // inverse mod 2^16 + inverse *= 2 - denominator * inverse; // inverse mod 2^32 + inverse *= 2 - denominator * inverse; // inverse mod 2^64 + inverse *= 2 - denominator * inverse; // inverse mod 2^128 + inverse *= 2 - denominator * inverse; // inverse mod 2^256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is + // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; + } + } + + /** + * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { + uint256 result = mulDiv(x, y, denominator); + if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { + result += 1; + } + return result; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded + * towards zero. + * + * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). + */ + function sqrt(uint256 a) internal pure returns (uint256) { + if (a == 0) { + return 0; + } + + // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. + // + // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have + // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. + // + // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` + // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` + // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` + // + // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. + uint256 result = 1 << (log2(a) >> 1); + + // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, + // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at + // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision + // into the expected uint128 result. + unchecked { + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + result = (result + a / result) >> 1; + return min(result, a / result); + } + } + + /** + * @notice Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); + } + } + + /** + * @dev Return the log in base 2 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + function log2(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 128; + } + if (value >> 64 > 0) { + value >>= 64; + result += 64; + } + if (value >> 32 > 0) { + value >>= 32; + result += 32; + } + if (value >> 16 > 0) { + value >>= 16; + result += 16; + } + if (value >> 8 > 0) { + value >>= 8; + result += 8; + } + if (value >> 4 > 0) { + value >>= 4; + result += 4; + } + if (value >> 2 > 0) { + value >>= 2; + result += 2; + } + if (value >> 1 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 10 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10 ** 64) { + value /= 10 ** 64; + result += 64; + } + if (value >= 10 ** 32) { + value /= 10 ** 32; + result += 32; + } + if (value >= 10 ** 16) { + value /= 10 ** 16; + result += 16; + } + if (value >= 10 ** 8) { + value /= 10 ** 8; + result += 8; + } + if (value >= 10 ** 4) { + value /= 10 ** 4; + result += 4; + } + if (value >= 10 ** 2) { + value /= 10 ** 2; + result += 2; + } + if (value >= 10 ** 1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); + } + } + + /** + * @dev Return the log in base 256 of a positive value rounded towards zero. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + function log256(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >> 128 > 0) { + value >>= 128; + result += 16; + } + if (value >> 64 > 0) { + value >>= 64; + result += 8; + } + if (value >> 32 > 0) { + value >>= 32; + result += 4; + } + if (value >> 16 > 0) { + value >>= 16; + result += 2; + } + if (value >> 8 > 0) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 256, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); + } + } + + /** + * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. + */ + function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { + return uint8(rounding) % 2 == 1; + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +/** + * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeCast { + /** + * @dev Value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); + + /** + * @dev An int value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedIntToUint(int256 value); + + /** + * @dev Value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); + + /** + * @dev An uint value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedUintToInt(uint256 value); + + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toUint248(uint256 value) internal pure returns (uint248) { + if (value > type(uint248).max) { + revert SafeCastOverflowedUintDowncast(248, value); + } + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toUint240(uint256 value) internal pure returns (uint240) { + if (value > type(uint240).max) { + revert SafeCastOverflowedUintDowncast(240, value); + } + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toUint232(uint256 value) internal pure returns (uint232) { + if (value > type(uint232).max) { + revert SafeCastOverflowedUintDowncast(232, value); + } + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + if (value > type(uint224).max) { + revert SafeCastOverflowedUintDowncast(224, value); + } + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toUint216(uint256 value) internal pure returns (uint216) { + if (value > type(uint216).max) { + revert SafeCastOverflowedUintDowncast(216, value); + } + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toUint208(uint256 value) internal pure returns (uint208) { + if (value > type(uint208).max) { + revert SafeCastOverflowedUintDowncast(208, value); + } + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toUint200(uint256 value) internal pure returns (uint200) { + if (value > type(uint200).max) { + revert SafeCastOverflowedUintDowncast(200, value); + } + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toUint192(uint256 value) internal pure returns (uint192) { + if (value > type(uint192).max) { + revert SafeCastOverflowedUintDowncast(192, value); + } + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toUint184(uint256 value) internal pure returns (uint184) { + if (value > type(uint184).max) { + revert SafeCastOverflowedUintDowncast(184, value); + } + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toUint176(uint256 value) internal pure returns (uint176) { + if (value > type(uint176).max) { + revert SafeCastOverflowedUintDowncast(176, value); + } + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toUint168(uint256 value) internal pure returns (uint168) { + if (value > type(uint168).max) { + revert SafeCastOverflowedUintDowncast(168, value); + } + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toUint160(uint256 value) internal pure returns (uint160) { + if (value > type(uint160).max) { + revert SafeCastOverflowedUintDowncast(160, value); + } + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toUint152(uint256 value) internal pure returns (uint152) { + if (value > type(uint152).max) { + revert SafeCastOverflowedUintDowncast(152, value); + } + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toUint144(uint256 value) internal pure returns (uint144) { + if (value > type(uint144).max) { + revert SafeCastOverflowedUintDowncast(144, value); + } + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toUint136(uint256 value) internal pure returns (uint136) { + if (value > type(uint136).max) { + revert SafeCastOverflowedUintDowncast(136, value); + } + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + if (value > type(uint128).max) { + revert SafeCastOverflowedUintDowncast(128, value); + } + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toUint120(uint256 value) internal pure returns (uint120) { + if (value > type(uint120).max) { + revert SafeCastOverflowedUintDowncast(120, value); + } + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toUint112(uint256 value) internal pure returns (uint112) { + if (value > type(uint112).max) { + revert SafeCastOverflowedUintDowncast(112, value); + } + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toUint104(uint256 value) internal pure returns (uint104) { + if (value > type(uint104).max) { + revert SafeCastOverflowedUintDowncast(104, value); + } + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + if (value > type(uint96).max) { + revert SafeCastOverflowedUintDowncast(96, value); + } + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toUint88(uint256 value) internal pure returns (uint88) { + if (value > type(uint88).max) { + revert SafeCastOverflowedUintDowncast(88, value); + } + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toUint80(uint256 value) internal pure returns (uint80) { + if (value > type(uint80).max) { + revert SafeCastOverflowedUintDowncast(80, value); + } + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toUint72(uint256 value) internal pure returns (uint72) { + if (value > type(uint72).max) { + revert SafeCastOverflowedUintDowncast(72, value); + } + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + if (value > type(uint64).max) { + revert SafeCastOverflowedUintDowncast(64, value); + } + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toUint56(uint256 value) internal pure returns (uint56) { + if (value > type(uint56).max) { + revert SafeCastOverflowedUintDowncast(56, value); + } + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toUint48(uint256 value) internal pure returns (uint48) { + if (value > type(uint48).max) { + revert SafeCastOverflowedUintDowncast(48, value); + } + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toUint40(uint256 value) internal pure returns (uint40) { + if (value > type(uint40).max) { + revert SafeCastOverflowedUintDowncast(40, value); + } + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + if (value > type(uint32).max) { + revert SafeCastOverflowedUintDowncast(32, value); + } + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toUint24(uint256 value) internal pure returns (uint24) { + if (value > type(uint24).max) { + revert SafeCastOverflowedUintDowncast(24, value); + } + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + if (value > type(uint16).max) { + revert SafeCastOverflowedUintDowncast(16, value); + } + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toUint8(uint256 value) internal pure returns (uint8) { + if (value > type(uint8).max) { + revert SafeCastOverflowedUintDowncast(8, value); + } + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + if (value < 0) { + revert SafeCastOverflowedIntToUint(value); + } + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(248, value); + } + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(240, value); + } + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(232, value); + } + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(224, value); + } + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(216, value); + } + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(208, value); + } + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(200, value); + } + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(192, value); + } + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(184, value); + } + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(176, value); + } + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(168, value); + } + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(160, value); + } + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(152, value); + } + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(144, value); + } + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(136, value); + } + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(128, value); + } + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(120, value); + } + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(112, value); + } + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(104, value); + } + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(96, value); + } + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(88, value); + } + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(80, value); + } + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(72, value); + } + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(64, value); + } + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(56, value); + } + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(48, value); + } + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(40, value); + } + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(32, value); + } + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(24, value); + } + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(16, value); + } + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(8, value); + } + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + if (value > uint256(type(int256).max)) { + revert SafeCastOverflowedUintToInt(value); + } + return int256(value); + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/math/SignedMath.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) + +/** + * @dev Standard signed math utilities missing in the Solidity language. + */ +library SignedMath { + /** + * @dev Returns the largest of two signed numbers. + */ + function max(int256 a, int256 b) internal pure returns (int256) { + return a > b ? a : b; + } + + /** + * @dev Returns the smallest of two signed numbers. + */ + function min(int256 a, int256 b) internal pure returns (int256) { + return a < b ? a : b; + } + + /** + * @dev Returns the average of two signed numbers without overflow. + * The result is rounded towards zero. + */ + function average(int256 a, int256 b) internal pure returns (int256) { + // Formula from the book "Hacker's Delight" + int256 x = (a & b) + ((a ^ b) >> 1); + return x + (int256(uint256(x) >> 255) & (a ^ b)); + } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // must be unchecked in order to support `n = type(int256).min` + return uint256(n >= 0 ? n : -n); + } + } +} + +// src/interfaces/IYieldDistributor.sol + +/** + * @title `YieldDistributor` interface + */ +interface IYieldDistributor { + /// @notice The error emitted when attempting to add a project that is already in the `projects` array + error AlreadyMemberProject(); + /// @notice The error emitted when a user attempts to vote without the minimum required voting power + error BelowMinRequiredVotingPower(); + /// @notice The error emitted when attempting to calculate voting power for a period that has not yet ended + error EndAfterCurrentBlock(); + /// @notice The error emitted when attempting to vote with a point value greater than `pointsMax` + error ExceedsMaxPoints(); + /// @notice The error emitted when attempting to vote with an incorrect number of projects + error IncorrectNumberOfProjects(); + /// @notice The error emitted when attempting to instantiate a variable with a zero value + error MustBeGreaterThanZero(); + /// @notice The error emitted when attempting to add or remove a project that is already queued for addition or removal + error ProjectAlreadyQueued(); + /// @notice The error emitted when attempting to remove a project that is not in the `projects` array + error ProjectNotFound(); + /// @notice The error emitted when attempting to calculate voting power for a period with a start block greater than the end block + error StartMustBeBeforeEnd(); + /// @notice The error emitted when attempting to distribute yield when access conditions are not met + error YieldNotResolved(); + /// @notice The error emitted if a user with zero points attempts to cast votes + error ZeroVotePoints(); + + /// @notice The event emitted when an account casts a vote + event BreadHolderVoted(address indexed account, uint256[] points, address[] projects); + /// @notice The event emitted when a project is added as eligibile for yield distribution + event ProjectAdded(address project); + /// @notice The event emitted when a project is removed as eligibile for yield distribution + event ProjectRemoved(address project); + /// @notice The event emitted when yield is distributed + event YieldDistributed(uint256 yield, uint256 totalVotes, uint256[] projectDistributions); +} + +// lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract ContextUpgradeable is Initializable { + function __Context_init() internal onlyInitializing {} + + function __Context_init_unchained() internal onlyInitializing {} + + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/utils/NoncesUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol) + +/** + * @dev Provides tracking nonces for addresses. Nonces will only increment. + */ +abstract contract NoncesUpgradeable is Initializable { + /** + * @dev The nonce used for an `account` is not the expected current nonce. + */ + error InvalidAccountNonce(address account, uint256 currentNonce); + + /// @custom:storage-location erc7201:openzeppelin.storage.Nonces + struct NoncesStorage { + mapping(address account => uint256) _nonces; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Nonces")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant NoncesStorageLocation = 0x5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb00; + + function _getNoncesStorage() private pure returns (NoncesStorage storage $) { + assembly { + $.slot := NoncesStorageLocation + } + } + + function __Nonces_init() internal onlyInitializing {} + + function __Nonces_init_unchained() internal onlyInitializing {} + /** + * @dev Returns the next unused nonce for an address. + */ + + function nonces(address owner) public view virtual returns (uint256) { + NoncesStorage storage $ = _getNoncesStorage(); + return $._nonces[owner]; + } + + /** + * @dev Consumes a nonce. + * + * Returns the current value and increments nonce. + */ + function _useNonce(address owner) internal virtual returns (uint256) { + NoncesStorage storage $ = _getNoncesStorage(); + // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be + // decremented or reset. This guarantees that the nonce never overflows. + unchecked { + // It is important to do x++ and not ++x here. + return $._nonces[owner]++; + } + } + + /** + * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`. + */ + function _useCheckedNonce(address owner, uint256 nonce) internal virtual { + uint256 current = _useNonce(owner); + if (nonce != current) { + revert InvalidAccountNonce(owner, current); + } + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) + +/** + * @dev Interface for the optional metadata functions from the ERC20 standard. + */ +interface IERC20Metadata is IERC20 { + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/structs/Checkpoints.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/Checkpoints.sol) +// This file was procedurally generated from scripts/generate/templates/Checkpoints.js. + +/** + * @dev This library defines the `Trace*` struct, for checkpointing values as they change at different points in + * time, and later looking up past values by block number. See {Votes} as an example. + * + * To create a history of checkpoints define a variable type `Checkpoints.Trace*` in your contract, and store a new + * checkpoint for the current transaction block using the {push} function. + */ +library Checkpoints { + /** + * @dev A value was attempted to be inserted on a past checkpoint. + */ + error CheckpointUnorderedInsertion(); + + struct Trace224 { + Checkpoint224[] _checkpoints; + } + + struct Checkpoint224 { + uint32 _key; + uint224 _value; + } + + /** + * @dev Pushes a (`key`, `value`) pair into a Trace224 so that it is stored as the checkpoint. + * + * Returns previous value and new value. + * + * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint32).max` key set will disable the + * library. + */ + function push(Trace224 storage self, uint32 key, uint224 value) internal returns (uint224, uint224) { + return _insert(self._checkpoints, key, value); + } + + /** + * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if + * there is none. + */ + function lowerLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { + uint256 len = self._checkpoints.length; + uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + */ + function upperLookup(Trace224 storage self, uint32 key) internal view returns (uint224) { + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + * + * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * keys). + */ + function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) { + uint256 len = self._checkpoints.length; + + uint256 low = 0; + uint256 high = len; + + if (len > 5) { + uint256 mid = len - Math.sqrt(len); + if (key < _unsafeAccess(self._checkpoints, mid)._key) { + high = mid; + } else { + low = mid + 1; + } + } + + uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); + + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(Trace224 storage self) internal view returns (uint224) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint(Trace224 storage self) internal view returns (bool exists, uint32 _key, uint224 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint224 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._key, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(Trace224 storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Returns checkpoint at given position. + */ + function at(Trace224 storage self, uint32 pos) internal view returns (Checkpoint224 memory) { + return self._checkpoints[pos]; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) private returns (uint224, uint224) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint224 memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + if (last._key > key) { + revert CheckpointUnorderedInsertion(); + } + + // Update or push new checkpoint + if (last._key == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint224({_key: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint224({_key: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` + * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive + * `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup(Checkpoint224[] storage self, uint32 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or + * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and + * exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup(Checkpoint224[] storage self, uint32 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint224[] storage self, uint256 pos) + private + pure + returns (Checkpoint224 storage result) + { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } + + struct Trace208 { + Checkpoint208[] _checkpoints; + } + + struct Checkpoint208 { + uint48 _key; + uint208 _value; + } + + /** + * @dev Pushes a (`key`, `value`) pair into a Trace208 so that it is stored as the checkpoint. + * + * Returns previous value and new value. + * + * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint48).max` key set will disable the + * library. + */ + function push(Trace208 storage self, uint48 key, uint208 value) internal returns (uint208, uint208) { + return _insert(self._checkpoints, key, value); + } + + /** + * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if + * there is none. + */ + function lowerLookup(Trace208 storage self, uint48 key) internal view returns (uint208) { + uint256 len = self._checkpoints.length; + uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + */ + function upperLookup(Trace208 storage self, uint48 key) internal view returns (uint208) { + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + * + * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * keys). + */ + function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) { + uint256 len = self._checkpoints.length; + + uint256 low = 0; + uint256 high = len; + + if (len > 5) { + uint256 mid = len - Math.sqrt(len); + if (key < _unsafeAccess(self._checkpoints, mid)._key) { + high = mid; + } else { + low = mid + 1; + } + } + + uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); + + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(Trace208 storage self) internal view returns (uint208) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint(Trace208 storage self) internal view returns (bool exists, uint48 _key, uint208 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint208 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._key, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(Trace208 storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Returns checkpoint at given position. + */ + function at(Trace208 storage self, uint32 pos) internal view returns (Checkpoint208 memory) { + return self._checkpoints[pos]; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint208[] storage self, uint48 key, uint208 value) private returns (uint208, uint208) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint208 memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + if (last._key > key) { + revert CheckpointUnorderedInsertion(); + } + + // Update or push new checkpoint + if (last._key == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint208({_key: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint208({_key: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` + * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive + * `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup(Checkpoint208[] storage self, uint48 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or + * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and + * exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup(Checkpoint208[] storage self, uint48 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint208[] storage self, uint256 pos) + private + pure + returns (Checkpoint208 storage result) + { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } + + struct Trace160 { + Checkpoint160[] _checkpoints; + } + + struct Checkpoint160 { + uint96 _key; + uint160 _value; + } + + /** + * @dev Pushes a (`key`, `value`) pair into a Trace160 so that it is stored as the checkpoint. + * + * Returns previous value and new value. + * + * IMPORTANT: Never accept `key` as a user input, since an arbitrary `type(uint96).max` key set will disable the + * library. + */ + function push(Trace160 storage self, uint96 key, uint160 value) internal returns (uint160, uint160) { + return _insert(self._checkpoints, key, value); + } + + /** + * @dev Returns the value in the first (oldest) checkpoint with key greater or equal than the search key, or zero if + * there is none. + */ + function lowerLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { + uint256 len = self._checkpoints.length; + uint256 pos = _lowerBinaryLookup(self._checkpoints, key, 0, len); + return pos == len ? 0 : _unsafeAccess(self._checkpoints, pos)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + */ + function upperLookup(Trace160 storage self, uint96 key) internal view returns (uint160) { + uint256 len = self._checkpoints.length; + uint256 pos = _upperBinaryLookup(self._checkpoints, key, 0, len); + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero + * if there is none. + * + * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * keys). + */ + function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) { + uint256 len = self._checkpoints.length; + + uint256 low = 0; + uint256 high = len; + + if (len > 5) { + uint256 mid = len - Math.sqrt(len); + if (key < _unsafeAccess(self._checkpoints, mid)._key) { + high = mid; + } else { + low = mid + 1; + } + } + + uint256 pos = _upperBinaryLookup(self._checkpoints, key, low, high); + + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns the value in the most recent checkpoint, or zero if there are no checkpoints. + */ + function latest(Trace160 storage self) internal view returns (uint160) { + uint256 pos = self._checkpoints.length; + return pos == 0 ? 0 : _unsafeAccess(self._checkpoints, pos - 1)._value; + } + + /** + * @dev Returns whether there is a checkpoint in the structure (i.e. it is not empty), and if so the key and value + * in the most recent checkpoint. + */ + function latestCheckpoint(Trace160 storage self) internal view returns (bool exists, uint96 _key, uint160 _value) { + uint256 pos = self._checkpoints.length; + if (pos == 0) { + return (false, 0, 0); + } else { + Checkpoint160 memory ckpt = _unsafeAccess(self._checkpoints, pos - 1); + return (true, ckpt._key, ckpt._value); + } + } + + /** + * @dev Returns the number of checkpoint. + */ + function length(Trace160 storage self) internal view returns (uint256) { + return self._checkpoints.length; + } + + /** + * @dev Returns checkpoint at given position. + */ + function at(Trace160 storage self, uint32 pos) internal view returns (Checkpoint160 memory) { + return self._checkpoints[pos]; + } + + /** + * @dev Pushes a (`key`, `value`) pair into an ordered list of checkpoints, either by inserting a new checkpoint, + * or by updating the last one. + */ + function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) private returns (uint160, uint160) { + uint256 pos = self.length; + + if (pos > 0) { + // Copying to memory is important here. + Checkpoint160 memory last = _unsafeAccess(self, pos - 1); + + // Checkpoint keys must be non-decreasing. + if (last._key > key) { + revert CheckpointUnorderedInsertion(); + } + + // Update or push new checkpoint + if (last._key == key) { + _unsafeAccess(self, pos - 1)._value = value; + } else { + self.push(Checkpoint160({_key: key, _value: value})); + } + return (last._value, value); + } else { + self.push(Checkpoint160({_key: key, _value: value})); + return (0, value); + } + } + + /** + * @dev Return the index of the last (most recent) checkpoint with key lower or equal than the search key, or `high` + * if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and exclusive + * `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _upperBinaryLookup(Checkpoint160[] storage self, uint96 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key > key) { + high = mid; + } else { + low = mid + 1; + } + } + return high; + } + + /** + * @dev Return the index of the first (oldest) checkpoint with key is greater or equal than the search key, or + * `high` if there is none. `low` and `high` define a section where to do the search, with inclusive `low` and + * exclusive `high`. + * + * WARNING: `high` should not be greater than the array's length. + */ + function _lowerBinaryLookup(Checkpoint160[] storage self, uint96 key, uint256 low, uint256 high) + private + view + returns (uint256) + { + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(self, mid)._key < key) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint160[] storage self, uint256 pos) + private + pure + returns (Checkpoint160 storage result) + { + assembly { + mstore(0, self.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is set to the address provided by the deployer. This can + * later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { + /// @custom:storage-location erc7201:openzeppelin.storage.Ownable + struct OwnableStorage { + address _owner; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; + + function _getOwnableStorage() private pure returns (OwnableStorage storage $) { + assembly { + $.slot := OwnableStorageLocation + } + } + + /** + * @dev The caller account is not authorized to perform an operation. + */ + error OwnableUnauthorizedAccount(address account); + + /** + * @dev The owner is not a valid owner account. (eg. `address(0)`) + */ + error OwnableInvalidOwner(address owner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the address provided by the deployer as the initial owner. + */ + function __Ownable_init(address initialOwner) internal onlyInitializing { + __Ownable_init_unchained(initialOwner); + } + + function __Ownable_init_unchained(address initialOwner) internal onlyInitializing { + if (initialOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(initialOwner); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + OwnableStorage storage $ = _getOwnableStorage(); + return $._owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + if (owner() != _msgSender()) { + revert OwnableUnauthorizedAccount(_msgSender()); + } + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby disabling any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + if (newOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + OwnableStorage storage $ = _getOwnableStorage(); + address oldOwner = $._owner; + $._owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/interfaces/IERC5805.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5805.sol) + +interface IERC5805 is IERC6372, IVotes {} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Strings.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant HEX_DIGITS = "0123456789abcdef"; + uint8 private constant ADDRESS_LENGTH = 20; + + /** + * @dev The `value` string doesn't fit in the specified `length`. + */ + error StringsInsufficientHexLength(uint256 value, uint256 length); + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + /// @solidity memory-safe-assembly + assembly { + ptr := add(buffer, add(32, length)) + } + while (true) { + ptr--; + /// @solidity memory-safe-assembly + assembly { + mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `int256` to its ASCII `string` decimal representation. + */ + function toStringSigned(int256 value) internal pure returns (string memory) { + return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + uint256 localValue = value; + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = HEX_DIGITS[localValue & 0xf]; + localValue >>= 4; + } + if (localValue != 0) { + revert StringsInsufficientHexLength(value, length); + } + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal + * representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); + } + + /** + * @dev Returns true if the two strings are equal. + */ + function equal(string memory a, string memory b) internal pure returns (bool) { + return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/types/Time.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol) + +/** + * @dev This library provides helpers for manipulating time-related objects. + * + * It uses the following types: + * - `uint48` for timepoints + * - `uint32` for durations + * + * While the library doesn't provide specific types for timepoints and duration, it does provide: + * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point + * - additional helper functions + */ +library Time { + using Time for *; + + /** + * @dev Get the block timestamp as a Timepoint. + */ + function timestamp() internal view returns (uint48) { + return SafeCast.toUint48(block.timestamp); + } + + /** + * @dev Get the block number as a Timepoint. + */ + function blockNumber() internal view returns (uint48) { + return SafeCast.toUint48(block.number); + } + + // ==================================================== Delay ===================================================== + /** + * @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the + * future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value. + * This allows updating the delay applied to some operation while keeping some guarantees. + * + * In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for + * some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set + * the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should + * still apply for some time. + * + * + * The `Delay` type is 112 bits long, and packs the following: + * + * ``` + * | [uint48]: effect date (timepoint) + * | | [uint32]: value before (duration) + * ↓ ↓ ↓ [uint32]: value after (duration) + * 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC + * ``` + * + * NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently + * supported. + */ + type Delay is uint112; + + /** + * @dev Wrap a duration into a Delay to add the one-step "update in the future" feature + */ + function toDelay(uint32 duration) internal pure returns (Delay) { + return Delay.wrap(duration); + } + + /** + * @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled + * change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered. + */ + function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) { + (uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack(); + return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect); + } + + /** + * @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the + * effect timepoint is 0, then the pending value should not be considered. + */ + function getFull(Delay self) internal view returns (uint32, uint32, uint48) { + return _getFullAt(self, timestamp()); + } + + /** + * @dev Get the current value. + */ + function get(Delay self) internal view returns (uint32) { + (uint32 delay,,) = self.getFull(); + return delay; + } + + /** + * @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to + * enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the + * new delay becomes effective. + */ + function withUpdate(Delay self, uint32 newValue, uint32 minSetback) + internal + view + returns (Delay updatedDelay, uint48 effect) + { + uint32 value = self.get(); + uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0)); + effect = timestamp() + setback; + return (pack(value, newValue, effect), effect); + } + + /** + * @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint). + */ + function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) { + uint112 raw = Delay.unwrap(self); + + valueAfter = uint32(raw); + valueBefore = uint32(raw >> 32); + effect = uint48(raw >> 64); + + return (valueBefore, valueAfter, effect); + } + + /** + * @dev pack the components into a Delay object. + */ + function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) { + return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter)); + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + /** + * @dev An operation with an ERC20 token failed. + */ + error SafeERC20FailedOperation(address token); + + /** + * @dev Indicates a failed `decreaseAllowance` request. + */ + error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); + + /** + * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, + * non-reverting calls are assumed to be successful. + */ + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); + } + + /** + * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the + * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. + */ + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); + } + + /** + * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, + * non-reverting calls are assumed to be successful. + */ + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 oldAllowance = token.allowance(address(this), spender); + forceApprove(token, spender, oldAllowance + value); + } + + /** + * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no + * value, non-reverting calls are assumed to be successful. + */ + function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { + unchecked { + uint256 currentAllowance = token.allowance(address(this), spender); + if (currentAllowance < requestedDecrease) { + revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); + } + forceApprove(token, spender, currentAllowance - requestedDecrease); + } + } + + /** + * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, + * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval + * to be set to zero before setting it to a non-zero value, such as USDT. + */ + function forceApprove(IERC20 token, address spender, uint256 value) internal { + bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); + + if (!_callOptionalReturnBool(token, approvalCall)) { + _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); + _callOptionalReturn(token, approvalCall); + } + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data); + if (returndata.length != 0 && !abi.decode(returndata, (bool))) { + revert SafeERC20FailedOperation(address(token)); + } + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + * + * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. + */ + function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false + // and not revert is the subcall reverts. + + (bool success, bytes memory returndata) = address(token).call(data); + return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; + } +} + +// lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) + +/** + * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. + * + * The library provides methods for generating a hash of a message that conforms to the + * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] + * specifications. + */ +library MessageHashUtils { + /** + * @dev Returns the keccak256 digest of an EIP-191 signed data with version + * `0x45` (`personal_sign` messages). + * + * The digest is calculated by prefixing a bytes32 `messageHash` with + * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the + * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. + * + * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with + * keccak256, although any bytes32 value can be safely used because the final digest will + * be re-hashed. + * + * See {ECDSA-recover}. + */ + function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash + mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix + digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) + } + } + + /** + * @dev Returns the keccak256 digest of an EIP-191 signed data with version + * `0x45` (`personal_sign` messages). + * + * The digest is calculated by prefixing an arbitrary `message` with + * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the + * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. + * + * See {ECDSA-recover}. + */ + function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { + return + keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); + } + + /** + * @dev Returns the keccak256 digest of an EIP-191 signed data with version + * `0x00` (data with intended validator). + * + * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended + * `validator` address. Then hashing the result. + * + * See {ECDSA-recover}. + */ + function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { + return keccak256(abi.encodePacked(hex"1900", validator, data)); + } + + /** + * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). + * + * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with + * `\x19\x01` and hashing the result. It corresponds to the hash signed by the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. + * + * See {ECDSA-recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) + mstore(ptr, hex"1901") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * + * TIP: For a detailed writeup see our guide + * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * The default value of {decimals} is 18. To change this, you should override + * this function so it returns a different value. + * + * We have followed general OpenZeppelin Contracts guidelines: functions revert + * instead returning `false` on failure. This behavior is nonetheless + * conventional and does not conflict with the expectations of ERC20 + * applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + */ +abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors { + /// @custom:storage-location erc7201:openzeppelin.storage.ERC20 + struct ERC20Storage { + mapping(address account => uint256) _balances; + mapping(address account => mapping(address spender => uint256)) _allowances; + uint256 _totalSupply; + string _name; + string _symbol; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00; + + function _getERC20Storage() private pure returns (ERC20Storage storage $) { + assembly { + $.slot := ERC20StorageLocation + } + } + + /** + * @dev Sets the values for {name} and {symbol}. + * + * All two of these values are immutable: they can only be set once during + * construction. + */ + function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { + __ERC20_init_unchained(name_, symbol_); + } + + function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { + ERC20Storage storage $ = _getERC20Storage(); + $._name = name_; + $._symbol = symbol_; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + ERC20Storage storage $ = _getERC20Storage(); + return $._name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + ERC20Storage storage $ = _getERC20Storage(); + return $._symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + ERC20Storage storage $ = _getERC20Storage(); + return $._totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + ERC20Storage storage $ = _getERC20Storage(); + return $._balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _transfer(owner, to, value); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual returns (uint256) { + ERC20Storage storage $ = _getERC20Storage(); + return $._allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 value) public virtual returns (bool) { + address owner = _msgSender(); + _approve(owner, spender, value); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + * + * Requirements: + * + * - `from` and `to` cannot be the zero address. + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for ``from``'s tokens of at least + * `value`. + */ + function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { + address spender = _msgSender(); + _spendAllowance(from, spender, value); + _transfer(from, to, value); + return true; + } + + /** + * @dev Moves a `value` amount of tokens from `from` to `to`. + * + * This internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _transfer(address from, address to, uint256 value) internal { + if (from == address(0)) { + revert ERC20InvalidSender(address(0)); + } + if (to == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(from, to, value); + } + + /** + * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` + * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding + * this function. + * + * Emits a {Transfer} event. + */ + function _update(address from, address to, uint256 value) internal virtual { + ERC20Storage storage $ = _getERC20Storage(); + if (from == address(0)) { + // Overflow check required: The rest of the code assumes that totalSupply never overflows + $._totalSupply += value; + } else { + uint256 fromBalance = $._balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } + unchecked { + // Overflow not possible: value <= fromBalance <= totalSupply. + $._balances[from] = fromBalance - value; + } + } + + if (to == address(0)) { + unchecked { + // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + $._totalSupply -= value; + } + } else { + unchecked { + // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. + $._balances[to] += value; + } + } + + emit Transfer(from, to, value); + } + + /** + * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). + * Relies on the `_update` mechanism + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead. + */ + function _mint(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidReceiver(address(0)); + } + _update(address(0), account, value); + } + + /** + * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. + * Relies on the `_update` mechanism. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * NOTE: This function is not virtual, {_update} should be overridden instead + */ + function _burn(address account, uint256 value) internal { + if (account == address(0)) { + revert ERC20InvalidSender(address(0)); + } + _update(account, address(0), value); + } + + /** + * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address owner, address spender, uint256 value) internal { + _approve(owner, spender, value, true); + } + + /** + * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. + * + * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by + * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any + * `Approval` event during `transferFrom` operations. + * + * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to + * true using the following override: + * ``` + * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { + * super._approve(owner, spender, value, true); + * } + * ``` + * + * Requirements are the same as {_approve}. + */ + function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { + ERC20Storage storage $ = _getERC20Storage(); + if (owner == address(0)) { + revert ERC20InvalidApprover(address(0)); + } + if (spender == address(0)) { + revert ERC20InvalidSpender(address(0)); + } + $._allowances[owner][spender] = value; + if (emitEvent) { + emit Approval(owner, spender, value); + } + } + + /** + * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * + * Does not update the allowance value in case of infinite allowance. + * Revert if not enough allowance is available. + * + * Does not emit an {Approval} event. + */ + function _spendAllowance(address owner, address spender, uint256 value) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(spender, currentAllowance, value); + } + unchecked { + _approve(owner, spender, currentAllowance - value, false); + } + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol) + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose + * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract + * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to + * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain + * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the + * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. + */ +abstract contract EIP712Upgradeable is Initializable, IERC5267 { + bytes32 private constant TYPE_HASH = + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + + /// @custom:storage-location erc7201:openzeppelin.storage.EIP712 + struct EIP712Storage { + /// @custom:oz-renamed-from _HASHED_NAME + bytes32 _hashedName; + /// @custom:oz-renamed-from _HASHED_VERSION + bytes32 _hashedVersion; + string _name; + string _version; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100; + + function _getEIP712Storage() private pure returns (EIP712Storage storage $) { + assembly { + $.slot := EIP712StorageLocation + } + } + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + function __EIP712_init(string memory name, string memory version) internal onlyInitializing { + __EIP712_init_unchained(name, version); + } + + function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing { + EIP712Storage storage $ = _getEIP712Storage(); + $._name = name; + $._version = version; + + // Reset prior values in storage if upgrading + $._hashedName = 0; + $._hashedVersion = 0; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + return _buildDomainSeparator(); + } + + function _buildDomainSeparator() private view returns (bytes32) { + return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); + } + + /** + * @dev See {IERC-5267}. + */ + function eip712Domain() + public + view + virtual + returns ( + bytes1 fields, + string memory name, + string memory version, + uint256 chainId, + address verifyingContract, + bytes32 salt, + uint256[] memory extensions + ) + { + EIP712Storage storage $ = _getEIP712Storage(); + // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized + // and the EIP712 domain is not reliable, as it will be missing name and version. + require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized"); + + return ( + hex"0f", // 01111 + _EIP712Name(), + _EIP712Version(), + block.chainid, + address(this), + bytes32(0), + new uint256[](0) + ); + } + + /** + * @dev The name parameter for the EIP712 domain. + * + * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs + * are a concern. + */ + function _EIP712Name() internal view virtual returns (string memory) { + EIP712Storage storage $ = _getEIP712Storage(); + return $._name; + } + + /** + * @dev The version parameter for the EIP712 domain. + * + * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs + * are a concern. + */ + function _EIP712Version() internal view virtual returns (string memory) { + EIP712Storage storage $ = _getEIP712Storage(); + return $._version; + } + + /** + * @dev The hash of the name parameter for the EIP712 domain. + * + * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead. + */ + function _EIP712NameHash() internal view returns (bytes32) { + EIP712Storage storage $ = _getEIP712Storage(); + string memory name = _EIP712Name(); + if (bytes(name).length > 0) { + return keccak256(bytes(name)); + } else { + // If the name is empty, the contract may have been upgraded without initializing the new storage. + // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design. + bytes32 hashedName = $._hashedName; + if (hashedName != 0) { + return hashedName; + } else { + return keccak256(""); + } + } + } + + /** + * @dev The hash of the version parameter for the EIP712 domain. + * + * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead. + */ + function _EIP712VersionHash() internal view returns (bytes32) { + EIP712Storage storage $ = _getEIP712Storage(); + string memory version = _EIP712Version(); + if (bytes(version).length > 0) { + return keccak256(bytes(version)); + } else { + // If the version is empty, the contract may have been upgraded without initializing the new storage. + // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design. + bytes32 hashedVersion = $._hashedVersion; + if (hashedVersion != 0) { + return hashedVersion; + } else { + return keccak256(""); + } + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/governance/utils/VotesUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/Votes.sol) + +/** + * @dev This is a base abstract contract that tracks voting units, which are a measure of voting power that can be + * transferred, and provides a system of vote delegation, where an account can delegate its voting units to a sort of + * "representative" that will pool delegated voting units from different accounts and can then use it to vote in + * decisions. In fact, voting units _must_ be delegated in order to count as actual votes, and an account has to + * delegate those votes to itself if it wishes to participate in decisions and does not have a trusted representative. + * + * This contract is often combined with a token contract such that voting units correspond to token units. For an + * example, see {ERC721Votes}. + * + * The full history of delegate votes is tracked on-chain so that governance protocols can consider votes as distributed + * at a particular block number to protect against flash loans and double voting. The opt-in delegate system makes the + * cost of this history tracking optional. + * + * When using this module the derived contract must implement {_getVotingUnits} (for example, make it return + * {ERC721-balanceOf}), and can use {_transferVotingUnits} to track a change in the distribution of those units (in the + * previous example, it would be included in {ERC721-_update}). + */ +abstract contract VotesUpgradeable is + Initializable, + ContextUpgradeable, + EIP712Upgradeable, + NoncesUpgradeable, + IERC5805 +{ + using Checkpoints for Checkpoints.Trace208; + + bytes32 private constant DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + /// @custom:storage-location erc7201:openzeppelin.storage.Votes + struct VotesStorage { + mapping(address account => address) _delegatee; + mapping(address delegatee => Checkpoints.Trace208) _delegateCheckpoints; + Checkpoints.Trace208 _totalCheckpoints; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Votes")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant VotesStorageLocation = 0xe8b26c30fad74198956032a3533d903385d56dd795af560196f9c78d4af40d00; + + function _getVotesStorage() private pure returns (VotesStorage storage $) { + assembly { + $.slot := VotesStorageLocation + } + } + + /** + * @dev The clock was incorrectly modified. + */ + error ERC6372InconsistentClock(); + + /** + * @dev Lookup to future votes is not available. + */ + error ERC5805FutureLookup(uint256 timepoint, uint48 clock); + + function __Votes_init() internal onlyInitializing {} + + function __Votes_init_unchained() internal onlyInitializing {} + /** + * @dev Clock used for flagging checkpoints. Can be overridden to implement timestamp based + * checkpoints (and voting), in which case {CLOCK_MODE} should be overridden as well to match. + */ + + function clock() public view virtual returns (uint48) { + return Time.blockNumber(); + } + + /** + * @dev Machine-readable description of the clock as specified in EIP-6372. + */ + // solhint-disable-next-line func-name-mixedcase + function CLOCK_MODE() public view virtual returns (string memory) { + // Check that the clock was not modified + if (clock() != Time.blockNumber()) { + revert ERC6372InconsistentClock(); + } + return "mode=blocknumber&from=default"; + } + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) public view virtual returns (uint256) { + VotesStorage storage $ = _getVotesStorage(); + return $._delegateCheckpoints[account].latest(); + } + + /** + * @dev Returns the amount of votes that `account` had at a specific moment in the past. If the `clock()` is + * configured to use block numbers, this will return the value at the end of the corresponding block. + * + * Requirements: + * + * - `timepoint` must be in the past. If operating using block numbers, the block must be already mined. + */ + function getPastVotes(address account, uint256 timepoint) public view virtual returns (uint256) { + VotesStorage storage $ = _getVotesStorage(); + uint48 currentTimepoint = clock(); + if (timepoint >= currentTimepoint) { + revert ERC5805FutureLookup(timepoint, currentTimepoint); + } + return $._delegateCheckpoints[account].upperLookupRecent(SafeCast.toUint48(timepoint)); + } + + /** + * @dev Returns the total supply of votes available at a specific moment in the past. If the `clock()` is + * configured to use block numbers, this will return the value at the end of the corresponding block. + * + * NOTE: This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. + * Votes that have not been delegated are still part of total supply, even though they would not participate in a + * vote. + * + * Requirements: + * + * - `timepoint` must be in the past. If operating using block numbers, the block must be already mined. + */ + function getPastTotalSupply(uint256 timepoint) public view virtual returns (uint256) { + VotesStorage storage $ = _getVotesStorage(); + uint48 currentTimepoint = clock(); + if (timepoint >= currentTimepoint) { + revert ERC5805FutureLookup(timepoint, currentTimepoint); + } + return $._totalCheckpoints.upperLookupRecent(SafeCast.toUint48(timepoint)); + } + + /** + * @dev Returns the current total supply of votes. + */ + function _getTotalSupply() internal view virtual returns (uint256) { + VotesStorage storage $ = _getVotesStorage(); + return $._totalCheckpoints.latest(); + } + + /** + * @dev Returns the delegate that `account` has chosen. + */ + function delegates(address account) public view virtual returns (address) { + VotesStorage storage $ = _getVotesStorage(); + return $._delegatee[account]; + } + + /** + * @dev Delegates votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public virtual { + address account = _msgSender(); + _delegate(account, delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee`. + */ + function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) + public + virtual + { + if (block.timestamp > expiry) { + revert VotesExpiredSignature(expiry); + } + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry))), v, r, s + ); + _useCheckedNonce(signer, nonce); + _delegate(signer, delegatee); + } + + /** + * @dev Delegate all of `account`'s voting units to `delegatee`. + * + * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. + */ + function _delegate(address account, address delegatee) internal virtual { + VotesStorage storage $ = _getVotesStorage(); + address oldDelegate = delegates(account); + $._delegatee[account] = delegatee; + + emit DelegateChanged(account, oldDelegate, delegatee); + _moveDelegateVotes(oldDelegate, delegatee, _getVotingUnits(account)); + } + + /** + * @dev Transfers, mints, or burns voting units. To register a mint, `from` should be zero. To register a burn, `to` + * should be zero. Total supply of voting units will be adjusted with mints and burns. + */ + function _transferVotingUnits(address from, address to, uint256 amount) internal virtual { + VotesStorage storage $ = _getVotesStorage(); + if (from == address(0)) { + _push($._totalCheckpoints, _add, SafeCast.toUint208(amount)); + } + if (to == address(0)) { + _push($._totalCheckpoints, _subtract, SafeCast.toUint208(amount)); + } + _moveDelegateVotes(delegates(from), delegates(to), amount); + } + + /** + * @dev Moves delegated votes from one delegate to another. + */ + function _moveDelegateVotes(address from, address to, uint256 amount) private { + VotesStorage storage $ = _getVotesStorage(); + if (from != to && amount > 0) { + if (from != address(0)) { + (uint256 oldValue, uint256 newValue) = + _push($._delegateCheckpoints[from], _subtract, SafeCast.toUint208(amount)); + emit DelegateVotesChanged(from, oldValue, newValue); + } + if (to != address(0)) { + (uint256 oldValue, uint256 newValue) = + _push($._delegateCheckpoints[to], _add, SafeCast.toUint208(amount)); + emit DelegateVotesChanged(to, oldValue, newValue); + } + } + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function _numCheckpoints(address account) internal view virtual returns (uint32) { + VotesStorage storage $ = _getVotesStorage(); + return SafeCast.toUint32($._delegateCheckpoints[account].length()); + } + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function _checkpoints(address account, uint32 pos) + internal + view + virtual + returns (Checkpoints.Checkpoint208 memory) + { + VotesStorage storage $ = _getVotesStorage(); + return $._delegateCheckpoints[account].at(pos); + } + + function _push( + Checkpoints.Trace208 storage store, + function(uint208, uint208) view returns (uint208) op, + uint208 delta + ) private returns (uint208, uint208) { + return store.push(clock(), op(store.latest(), delta)); + } + + function _add(uint208 a, uint208 b) private pure returns (uint208) { + return a + b; + } + + function _subtract(uint208 a, uint208 b) private pure returns (uint208) { + return a - b; + } + + /** + * @dev Must return the voting units held by an account. + */ + function _getVotingUnits(address) internal view virtual returns (uint256); +} + +// lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Votes.sol) + +/** + * @dev Extension of ERC20 to support Compound-like voting and delegation. This version is more generic than Compound's, + * and supports token supply up to 2^208^ - 1, while COMP is limited to 2^96^ - 1. + * + * NOTE: This contract does not provide interface compatibility with Compound's COMP token. + * + * This extension keeps a history (checkpoints) of each account's vote power. Vote power can be delegated either + * by calling the {delegate} function directly, or by providing a signature to be used with {delegateBySig}. Voting + * power can be queried through the public accessors {getVotes} and {getPastVotes}. + * + * By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it + * requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + */ +abstract contract ERC20VotesUpgradeable is Initializable, ERC20Upgradeable, VotesUpgradeable { + /** + * @dev Total supply cap has been exceeded, introducing a risk of votes overflowing. + */ + error ERC20ExceededSafeSupply(uint256 increasedSupply, uint256 cap); + + function __ERC20Votes_init() internal onlyInitializing {} + + function __ERC20Votes_init_unchained() internal onlyInitializing {} + /** + * @dev Maximum token supply. Defaults to `type(uint208).max` (2^208^ - 1). + * + * This maximum is enforced in {_update}. It limits the total supply of the token, which is otherwise a uint256, + * so that checkpoints can be stored in the Trace208 structure used by {{Votes}}. Increasing this value will not + * remove the underlying limitation, and will cause {_update} to fail because of a math overflow in + * {_transferVotingUnits}. An override could be used to further restrict the total supply (to a lower value) if + * additional logic requires it. When resolving override conflicts on this function, the minimum should be + * returned. + */ + + function _maxSupply() internal view virtual returns (uint256) { + return type(uint208).max; + } + + /** + * @dev Move voting power when tokens are transferred. + * + * Emits a {IVotes-DelegateVotesChanged} event. + */ + function _update(address from, address to, uint256 value) internal virtual override { + super._update(from, to, value); + if (from == address(0)) { + uint256 supply = totalSupply(); + uint256 cap = _maxSupply(); + if (supply > cap) { + revert ERC20ExceededSafeSupply(supply, cap); + } + } + _transferVotingUnits(from, to, value); + } + + /** + * @dev Returns the voting units of an `account`. + * + * WARNING: Overriding this function may compromise the internal vote accounting. + * `ERC20Votes` assumes tokens map to voting units 1:1 and this is not easy to change. + */ + function _getVotingUnits(address account) internal view virtual override returns (uint256) { + return balanceOf(account); + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view virtual returns (uint32) { + return _numCheckpoints(account); + } + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view virtual returns (Checkpoints.Checkpoint208 memory) { + return _checkpoints(account, pos); + } +} + +// lib/bread-token-v2/src/Bread.sol + +// Bread - An ERC20 stablecoin fully collateralized by Gnosis Chain xDAI +// which earns yield via Gnosis Chain sDAI (aka sexyDAI) +// and points this yield to the Breadchain Ecosystem +// implemented by: kassandra.eth + +contract Bread is ERC20VotesUpgradeable, OwnableUpgradeable, IBread { + using SafeERC20 for IERC20; + + address public yieldClaimer; + + error MintZero(); + error BurnZero(); + error ClaimZero(); + error YieldInsufficient(); + error IsCollateral(); + error NativeTransferFailed(); + error OnlyClaimers(); + error MismatchArray(); + error MismatchAmount(); + + IWXDAI public immutable wxDai; + ISXDAI public immutable sexyDai; + + event Minted(address receiver, uint256 amount); + event Burned(address receiver, uint256 amount); + event YieldClaimerSet(address yieldClaimer); + event ClaimedYield(uint256 amount); + + constructor(address _wxDai, address _sexyDai) { + wxDai = IWXDAI(_wxDai); + sexyDai = ISXDAI(_sexyDai); + } + + function initialize(string memory name_, string memory symbol_, address owner_) external initializer { + __ERC20_init(name_, symbol_); + __Ownable_init(owner_); + } + + function setYieldClaimer(address _yieldClaimer) external onlyOwner { + yieldClaimer = _yieldClaimer; + emit YieldClaimerSet(_yieldClaimer); + } + + function mint(address receiver) external payable { + uint256 val = msg.value; + if (val == 0) revert MintZero(); + + wxDai.deposit{value: val}(); + IERC20(address(wxDai)).safeIncreaseAllowance(address(sexyDai), val); + sexyDai.deposit(val, address(this)); + + _mint(receiver, val); + if (this.delegates(receiver) == address(0)) _delegate(receiver, receiver); + } + + function burn(uint256 amount, address receiver) external { + if (amount == 0) revert BurnZero(); + _burn(msg.sender, amount); + + sexyDai.withdraw(amount, address(this), address(this)); + wxDai.withdraw(amount); + _nativeTransfer(receiver, amount); + + emit Burned(receiver, amount); + } + + function claimYield(uint256 amount, address receiver) external { + if (msg.sender != owner() && msg.sender != yieldClaimer) revert OnlyClaimers(); + if (amount == 0) revert ClaimZero(); + uint256 yield = _yieldAccrued(); + if (yield < amount) revert YieldInsufficient(); + + _mint(receiver, amount); + _delegate(receiver, receiver); + + emit ClaimedYield(amount); + } + + function rescueToken(address tok, uint256 amount) external onlyOwner { + if (tok == address(sexyDai)) revert IsCollateral(); + IERC20(tok).safeTransfer(owner(), amount); + } + + function yieldAccrued() external view returns (uint256) { + return _yieldAccrued(); + } + + function _yieldAccrued() internal view returns (uint256) { + uint256 bal = IERC20(address(sexyDai)).balanceOf(address(this)); + uint256 assets = sexyDai.convertToAssets(bal); + uint256 supply = totalSupply(); + return assets > supply ? assets - supply : 0; + } + + function _nativeTransfer(address to, uint256 amount) internal { + bool success; + assembly { + // Transfer the ETH and store if it succeeded or not. + success := call(gas(), to, amount, 0, 0, 0, 0) + } + + if (!success) revert NativeTransferFailed(); + } + + function transfer(address recipient, uint256 amount) public override returns (bool) { + super.transfer(recipient, amount); + if (this.delegates(recipient) == address(0)) _delegate(recipient, recipient); + return true; + } + + function transferFrom(address from, address to, uint256 value) public override returns (bool) { + super.transferFrom(from, to, value); + if (this.delegates(to) == address(0)) _delegate(to, to); + return true; + } +} + +// src/YieldDistributor.sol + +/** + * @title Breadchain Yield Distributor + * @notice Distribute $BREAD yield to eligible member projects based on a voted distribution + * @author Breadchain Collective + * @custom:coauthor @RonTuretzky + * @custom:coauthor bagelface.eth + * @custom:coauthor prosalads.eth + * @custom:coauthor kassandra.eth + * @custom:coauthor theblockchainsocialist.eth + */ +contract YieldDistributor is IYieldDistributor, OwnableUpgradeable { + /// @notice The address of the $BREAD token contract + Bread public BREAD; + /// @notice The precision to use for calculations + uint256 public PRECISION; + /// @notice The minimum number of blocks between yield distributions + uint256 public cycleLength; + /// @notice The maximum number of points a voter can allocate to a project + uint256 public maxPoints; + /// @notice The minimum required voting power participants must have to cast a vote + uint256 public minRequiredVotingPower; + /// @notice The block number of the last yield distribution + uint256 public lastClaimedBlockNumber; + /// @notice The total number of votes cast in the current cycle + uint256 public currentVotes; + /// @notice Array of projects eligible for yield distribution + address[] public projects; + /// @notice Array of projects queued for addition to the next cycle + address[] public queuedProjectsForAddition; + /// @notice Array of projects queued for removal from the next cycle + address[] public queuedProjectsForRemoval; + /// @notice The voting power allocated to projects by voters in the current cycle + uint256[] public projectDistributions; + /// @notice The last block number in which a specified account cast a vote + mapping(address => uint256) public accountLastVoted; + /// @notice The voting power allocated to projects by voters in the current cycle + mapping(address => uint256[]) voterDistributions; + /// @notice How much of the yield is divided equally among projects + uint256 public yieldFixedSplitDivisor; + /// @notice The address of the `ButteredBread` token contract + ERC20VotesUpgradeable public BUTTERED_BREAD; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize( + address _bread, + address _butteredBread, + uint256 _precision, + uint256 _minRequiredVotingPower, + uint256 _maxPoints, + uint256 _cycleLength, + uint256 _yieldFixedSplitDivisor, + uint256 _lastClaimedBlockNumber, + address[] memory _projects + ) public initializer { + __Ownable_init(msg.sender); + if ( + _bread == address(0) || _butteredBread == address(0) || _precision == 0 || _minRequiredVotingPower == 0 + || _maxPoints == 0 || _cycleLength == 0 || _yieldFixedSplitDivisor == 0 || _lastClaimedBlockNumber == 0 + || _projects.length == 0 + ) { + revert MustBeGreaterThanZero(); + } + + BREAD = Bread(_bread); + BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); + PRECISION = _precision; + minRequiredVotingPower = _minRequiredVotingPower; + maxPoints = _maxPoints; + cycleLength = _cycleLength; + yieldFixedSplitDivisor = _yieldFixedSplitDivisor; + lastClaimedBlockNumber = _lastClaimedBlockNumber; + + projectDistributions = new uint256[](_projects.length); + projects = new address[](_projects.length); + for (uint256 i; i < _projects.length; ++i) { + projects[i] = _projects[i]; + } + } + + /** + * @notice Returns the current distribution of voting power for projects + * @return address[] The current eligible member projects + * @return uint256[] The current distribution of voting power for projects + */ + function getCurrentVotingDistribution() public view returns (address[] memory, uint256[] memory) { + return (projects, projectDistributions); + } + + /** + * @notice Return the current voting power of a user + * @param _account Address of the user to return the voting power for + * @return uint256 The voting power of the user + */ + function getCurrentVotingPower(address _account) public view returns (uint256) { + return this.getVotingPowerForPeriod( + BREAD, lastClaimedBlockNumber - cycleLength, lastClaimedBlockNumber, _account + ) + + this.getVotingPowerForPeriod( + BUTTERED_BREAD, lastClaimedBlockNumber - cycleLength, lastClaimedBlockNumber, _account + ); + } + + /** + * @notice Return the voting power for a specified user during a specified period of time + * @param _start Start time of the period to return the voting power for + * @param _end End time of the period to return the voting power for + * @param _account Address of user to return the voting power for + * @return uint256 Voting power of the specified user at the specified period of time + */ + function getVotingPowerForPeriod( + ERC20VotesUpgradeable _sourceContract, + uint256 _start, + uint256 _end, + address _account + ) external view returns (uint256) { + if (_start >= _end) revert StartMustBeBeforeEnd(); + if (_end > block.number) revert EndAfterCurrentBlock(); + + /// Initialized as the checkpoint count, but later used to track checkpoint index + uint32 _currentCheckpointIndex = _sourceContract.numCheckpoints(_account); + if (_currentCheckpointIndex == 0) return 0; + + /// No voting power if the first checkpoint is after the end of the interval + Checkpoints.Checkpoint208 memory _currentCheckpoint = _sourceContract.checkpoints(_account, 0); + if (_currentCheckpoint._key > _end) return 0; + + /// Find the latest checkpoint that is within the interval + do { + --_currentCheckpointIndex; + _currentCheckpoint = _sourceContract.checkpoints(_account, _currentCheckpointIndex); + } while (_currentCheckpoint._key > _end); + + /// Initialize voting power with the latest checkpoint thats within the interval (or nearest to it) + uint48 _latestKey = _currentCheckpoint._key < _start ? uint48(_start) : _currentCheckpoint._key; + uint256 _totalVotingPower = _currentCheckpoint._value * (_end - _latestKey); + + if (_latestKey == _start) return _totalVotingPower; + + for (uint32 i = _currentCheckpointIndex; i > 0;) { + /// Latest checkpoint voting power is calculated when initializing `_totalVotingPower`, so we pre-decrement the index here + _currentCheckpoint = _sourceContract.checkpoints(_account, --i); + + /// Add voting power for the sub-interval to the total + _totalVotingPower += _currentCheckpoint._value * (_latestKey - _currentCheckpoint._key); + + /// At the start of the interval, deduct voting power accrued before the interval and return the total + if (_currentCheckpoint._key <= _start) { + _totalVotingPower -= _currentCheckpoint._value * (_start - _currentCheckpoint._key); + break; + } + + _latestKey = _currentCheckpoint._key; + } + + return _totalVotingPower; + } + + /** + * @notice Determine if the yield distribution is available + * @dev Resolver function required for Powerpool job registration. For more details, see the Powerpool documentation: + * @dev https://docs.powerpool.finance/powerpool-and-poweragent-network/power-agent/user-guides-and-instructions/i-want-to-automate-my-tasks/job-registration-guide#resolver-job + * @return bool Flag indicating if the yield is able to be distributed + * @return bytes Calldata used by the resolver to distribute the yield + */ + function resolveYieldDistribution() public view returns (bool, bytes memory) { + uint256 _available_yield = BREAD.balanceOf(address(this)) + BREAD.yieldAccrued(); + if ( + /// No votes were cast + /// Already claimed this cycle + currentVotes == 0 || block.number < lastClaimedBlockNumber + cycleLength + || _available_yield / yieldFixedSplitDivisor < projects.length + ) { + /// Yield is insufficient + + return (false, new bytes(0)); + } else { + return (true, abi.encodePacked(this.distributeYield.selector)); + } + } + + /** + * @notice Distribute $BREAD yield to projects based on cast votes + */ + function distributeYield() public { + (bool _resolved,) = resolveYieldDistribution(); + if (!_resolved) revert YieldNotResolved(); + + BREAD.claimYield(BREAD.yieldAccrued(), address(this)); + lastClaimedBlockNumber = block.number; + uint256 balance = BREAD.balanceOf(address(this)); + uint256 _fixedYield = balance / yieldFixedSplitDivisor; + uint256 _baseSplit = _fixedYield / projects.length; + uint256 _votedYield = balance - _fixedYield; + + for (uint256 i; i < projects.length; ++i) { + uint256 _votedSplit = ((projectDistributions[i] * _votedYield * PRECISION) / currentVotes) / PRECISION; + BREAD.transfer(projects[i], _votedSplit + _baseSplit); + } + + _updateBreadchainProjects(); + + emit YieldDistributed(balance, currentVotes, projectDistributions); + + delete currentVotes; + projectDistributions = new uint256[](projects.length); + } + + /** + * @notice Cast votes for the distribution of $BREAD yield + * @param _points List of points as integers for each project + */ + function castVote(uint256[] calldata _points) public { + uint256 _currentVotingPower = getCurrentVotingPower(msg.sender); + + if (_currentVotingPower < minRequiredVotingPower) revert BelowMinRequiredVotingPower(); + + _castVote(msg.sender, _points, _currentVotingPower); + } + + /** + * @notice Internal function for casting votes for a specified user + * @param _account Address of user to cast votes for + * @param _points Basis points for calculating the amount of votes cast + * @param _votingPower Amount of voting power being cast + */ + function _castVote(address _account, uint256[] calldata _points, uint256 _votingPower) internal { + if (_points.length != projects.length) revert IncorrectNumberOfProjects(); + + uint256 _totalPoints; + for (uint256 i; i < _points.length; ++i) { + if (_points[i] > maxPoints) revert ExceedsMaxPoints(); + _totalPoints += _points[i]; + } + if (_totalPoints == 0) revert ZeroVotePoints(); + + bool _hasVotedInCycle = accountLastVoted[_account] > lastClaimedBlockNumber; + uint256[] storage _voterDistributions = voterDistributions[_account]; + if (!_hasVotedInCycle) { + delete voterDistributions[_account]; + currentVotes += _votingPower; + } + + 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; + projectDistributions[i] += _currentProjectDistribution; + _voterDistributions[i] = _currentProjectDistribution; + } + + accountLastVoted[_account] = block.number; + + emit BreadHolderVoted(_account, _points, projects); + } + + /** + * @notice Internal function for updating the project list + */ + function _updateBreadchainProjects() internal { + for (uint256 i; i < queuedProjectsForAddition.length; ++i) { + address _project = queuedProjectsForAddition[i]; + + projects.push(_project); + + emit ProjectAdded(_project); + } + + address[] memory _oldProjects = projects; + delete projects; + + for (uint256 i; i < _oldProjects.length; ++i) { + address _project = _oldProjects[i]; + bool _remove; + + for (uint256 j; j < queuedProjectsForRemoval.length; ++j) { + if (_project == queuedProjectsForRemoval[j]) { + _remove = true; + emit ProjectRemoved(_project); + break; + } + } + + if (!_remove) { + projects.push(_project); + } + } + + delete queuedProjectsForAddition; + delete queuedProjectsForRemoval; + } + + /** + * @notice Queue a new project to be added to the project list + * @param _project Project to be added to the project list + */ + function queueProjectAddition(address _project) public onlyOwner { + for (uint256 i; i < projects.length; ++i) { + if (projects[i] == _project) { + revert AlreadyMemberProject(); + } + } + + for (uint256 i; i < queuedProjectsForAddition.length; ++i) { + if (queuedProjectsForAddition[i] == _project) { + revert ProjectAlreadyQueued(); + } + } + + queuedProjectsForAddition.push(_project); + } + + /** + * @notice Queue an existing project to be removed from the project list + * @param _project Project to be removed from the project list + */ + function queueProjectRemoval(address _project) public onlyOwner { + bool _found = false; + for (uint256 i; i < projects.length; ++i) { + if (projects[i] == _project) { + _found = true; + } + } + + if (!_found) revert ProjectNotFound(); + + for (uint256 i; i < queuedProjectsForRemoval.length; ++i) { + if (queuedProjectsForRemoval[i] == _project) { + revert ProjectAlreadyQueued(); + } + } + + queuedProjectsForRemoval.push(_project); + } + + /** + * @notice Set a new minimum required voting power a user must have to vote + * @param _minRequiredVotingPower New minimum required voting power a user must have to vote + */ + function setMinRequiredVotingPower(uint256 _minRequiredVotingPower) public onlyOwner { + if (_minRequiredVotingPower == 0) revert MustBeGreaterThanZero(); + + minRequiredVotingPower = _minRequiredVotingPower; + } + + /** + * @notice Set a new maximum number of points a user can allocate to a project + * @param _maxPoints New maximum number of points a user can allocate to a project + */ + function setMaxPoints(uint256 _maxPoints) public onlyOwner { + if (_maxPoints == 0) revert MustBeGreaterThanZero(); + + maxPoints = _maxPoints; + } + + /** + * @notice Set a new cycle length in blocks + * @param _cycleLength New cycle length in blocks + */ + function setCycleLength(uint256 _cycleLength) public onlyOwner { + if (_cycleLength == 0) revert MustBeGreaterThanZero(); + + cycleLength = _cycleLength; + } + + /** + * @notice Set a new fixed split for the yield distribution + * @param _yieldFixedSplitDivisor New fixed split for the yield distribution + */ + function setyieldFixedSplitDivisor(uint256 _yieldFixedSplitDivisor) public onlyOwner { + if (_yieldFixedSplitDivisor == 0) revert MustBeGreaterThanZero(); + + yieldFixedSplitDivisor = _yieldFixedSplitDivisor; + } + + /** + * @notice Set the ButteredBread token contract + * @param _butteredBread Address of the ButteredBread token contract + */ + function setButteredBread(address _butteredBread) public onlyOwner { + BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); + } +}