From e535b85c44ea55faef73621a1fcdda35ce97f4d6 Mon Sep 17 00:00:00 2001 From: bagelface Date: Thu, 13 Nov 2025 15:33:22 -0500 Subject: [PATCH 1/8] feat: add gas killer to yield distributor contract --- .gitmodules | 3 + foundry.lock | 26 + foundry.toml | 2 +- lib/gas-killer-solidity-sdk | 1 + lib/openzeppelin-contracts | 2 +- lib/openzeppelin-contracts-upgradeable | 2 +- remappings.txt | 3 +- src/ButteredBread.sol | 13 +- src/VotingMultipliers.sol | 1 + src/YieldDistributor.sol | 33 +- .../multipliers/IDynamicNFTMultiplier.sol | 2 +- .../IOnChainProveableMultiplier.sol | 1 + .../multipliers/IProveableMultiplier.sol | 2 + src/multipliers/NFTMultiplier.sol | 3 +- src/multipliers/PermanentNFTMultiplier.sol | 1 + src/multipliers/VotingStreakMultiplier.sol | 3 +- test/ButteredBread.t.sol | 6 +- test/upgrades/v1.0.0/YieldDistributor.sol | 3 +- test/upgrades/v1.0.1/YieldDistributor.sol | 3 +- test/upgrades/v1.0.3/ButteredBread.sol | 2 +- test/upgrades/v1.0.4/ButteredBread.sol | 446 +++++++++--------- 21 files changed, 303 insertions(+), 255 deletions(-) create mode 100644 foundry.lock create mode 160000 lib/gas-killer-solidity-sdk diff --git a/.gitmodules b/.gitmodules index 5410de5d..84f654b2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "lib/openzeppelin-foundry-upgrades"] path = lib/openzeppelin-foundry-upgrades url = https://github.com/OpenZeppelin/openzeppelin-foundry-upgrades +[submodule "lib/gas-killer-solidity-sdk"] + path = lib/gas-killer-solidity-sdk + url = https://github.com/BreadchainCoop/gas-killer-solidity-sdk diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 00000000..f9b6a06c --- /dev/null +++ b/foundry.lock @@ -0,0 +1,26 @@ +{ + "lib/bread-token-v2": { + "rev": "77710c4f9809d0e5cda0c76764a8fd9f1fe65354" + }, + "lib/forge-std": { + "rev": "1714bee72e286e73f76e320d110e0eaf5c4e649d" + }, + "lib/gas-killer-solidity-sdk": { + "rev": "729ab010967afa8835b31704142e823e13680338" + }, + "lib/openzeppelin-contracts": { + "tag": { + "name": "v5.5.0", + "rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565" + } + }, + "lib/openzeppelin-contracts-upgradeable": { + "tag": { + "name": "v5.5.0", + "rev": "aa677e9d28ed78fc427ec47ba2baef2030c58e7c" + } + }, + "lib/openzeppelin-foundry-upgrades": { + "rev": "4cd15fc50b141c77d8cc9ff8efb44d00e841a299" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 7b3561c7..c72ab833 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,7 @@ src = "src" out = "out" libs = ["lib"] fs_permissions = [{ access = "read-write", path = "./" }] -solc_version = "0.8.25" +solc_version = "0.8.27" build_info = true ffi = true ast = true diff --git a/lib/gas-killer-solidity-sdk b/lib/gas-killer-solidity-sdk new file mode 160000 index 00000000..89762cdb --- /dev/null +++ b/lib/gas-killer-solidity-sdk @@ -0,0 +1 @@ +Subproject commit 89762cdb6992d1ff29bdbe36dad60f14026519a3 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index 01ef4489..fcbae539 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 01ef448981be9d20ca85f2faf6ebdf591ce409f3 +Subproject commit fcbae5394ae8ad52d8e580a3477db99814b9d565 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 723f8cab..aa677e9d 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 +Subproject commit aa677e9d28ed78fc427ec47ba2baef2030c58e7c diff --git a/remappings.txt b/remappings.txt index 8669e8a1..60eab02d 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,5 +2,4 @@ bread-token/=lib/bread-token-v2/ forge-std/=lib/forge-std/src/ @openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -openzeppelin-contracts/=lib/openzeppelin-contracts/ -openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/ \ No newline at end of file +gas-killer/=lib/gas-killer-solidity-sdk/ \ No newline at end of file diff --git a/src/ButteredBread.sol b/src/ButteredBread.sol index 42d167f4..95bcc3a1 100644 --- a/src/ButteredBread.sol +++ b/src/ButteredBread.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity 0.8.25; +pragma solidity ^0.8.25; -import {ERC20VotesUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; +import { + ERC20VotesUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; import {EIP712Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {ReentrancyGuardUpgradeable} from - "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {IButteredBread} from "src/interfaces/IButteredBread.sol"; import {IERC20Votes} from "src/interfaces/IERC20Votes.sol"; @@ -20,7 +20,7 @@ import {IERC20Votes} from "src/interfaces/IERC20Votes.sol"; * @custom:coauthor @daopunk * @custom:coauthor @bagelface */ -contract ButteredBread is IButteredBread, ERC20VotesUpgradeable, Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { +contract ButteredBread is IButteredBread, ERC20VotesUpgradeable, Ownable2StepUpgradeable, ReentrancyGuard { /// @notice Value used for calculating the precision of scaling factors uint256 public constant FIXED_POINT_PERCENT = 100; /// @notice `IERC20Votes` contract used for powering `ButteredBread` voting @@ -52,7 +52,6 @@ contract ButteredBread is IButteredBread, ERC20VotesUpgradeable, Ownable2StepUpg __EIP712_init(_initData.name, "1"); __ERC20Votes_init(); __Ownable_init(msg.sender); - __ReentrancyGuard_init(); for (uint256 i; i < _initData.liquidityPools.length; ++i) { scalingFactors[_initData.liquidityPools[i]] = _initData.scalingFactors[i]; diff --git a/src/VotingMultipliers.sol b/src/VotingMultipliers.sol index 5cbed3f2..25c8ef95 100644 --- a/src/VotingMultipliers.sol +++ b/src/VotingMultipliers.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.22; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + import {IVotingMultipliers, IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; import {MultiplierConstants} from "src/libraries/MultiplierConstants.sol"; diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index a7595325..0891b824 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.22; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {Checkpoints} from - "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; -import {ERC20VotesUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import { + ERC20VotesUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; +import {GasKillerSDK} from "gas-killer/flat/GasKillerSDK.flat.sol"; import {Bread} from "bread-token/src/Bread.sol"; import {IYieldDistributor} from "src/interfaces/IYieldDistributor.sol"; @@ -23,7 +24,7 @@ import {VotingMultipliers} from "src/VotingMultipliers.sol"; * @custom:coauthor github.com/daopunk * @custom:coauthor github.com/secbajor */ -contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingMultipliers { +contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingMultipliers, GasKillerSDK { /// @notice The address of the $BREAD token contract Bread public BREAD; /// @notice The precision to use for calculations @@ -58,7 +59,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM uint256 public previousCycleStartingBlock; /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { + constructor() GasKillerSDK(address(0), address(0)) { _disableInitializers(); } @@ -74,6 +75,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM address[] memory _projects ) public initializer { VotingMultipliers.initialize(); + if ( _bread == address(0) || _butteredBread == address(0) || _precision == 0 || _minRequiredVotingPower == 0 || _maxPoints == 0 || _cycleLength == 0 || _yieldFixedSplitDivisor == 0 || _lastClaimedBlockNumber == 0 @@ -98,6 +100,16 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM } } + /** + * @notice Initializes the GasKiller SDK + * @param _avsAddress The address of the AVS service manager + * @param _blsSignatureChecker The address of the BLS signature checker + */ + function initializeGasKiller(address _avsAddress, address _blsSignatureChecker) public reinitializer(1) { + _setAvsAddress(_avsAddress); + _setBlsSignatureChecker(_blsSignatureChecker); + } + /** * @notice Returns the current distribution of voting power for projects * @return address[] The current eligible member projects @@ -406,4 +418,13 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM function setButteredBread(address _butteredBread) public onlyOwner { BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); } + + /** + * @notice Allows the owner to set the AVS address + * @param newAvsAddress The new AVS address + * @dev Also updates the namespace for the contract + */ + function setAvsAddress(address newAvsAddress) external onlyOwner { + _setAvsAddress(newAvsAddress); + } } diff --git a/src/interfaces/multipliers/IDynamicNFTMultiplier.sol b/src/interfaces/multipliers/IDynamicNFTMultiplier.sol index 7c5bb423..d40cf5ae 100644 --- a/src/interfaces/multipliers/IDynamicNFTMultiplier.sol +++ b/src/interfaces/multipliers/IDynamicNFTMultiplier.sol @@ -2,10 +2,10 @@ 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 diff --git a/src/interfaces/multipliers/IOnChainProveableMultiplier.sol b/src/interfaces/multipliers/IOnChainProveableMultiplier.sol index 1dae00c7..10d89248 100644 --- a/src/interfaces/multipliers/IOnChainProveableMultiplier.sol +++ b/src/interfaces/multipliers/IOnChainProveableMultiplier.sol @@ -2,6 +2,7 @@ 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 { diff --git a/src/interfaces/multipliers/IProveableMultiplier.sol b/src/interfaces/multipliers/IProveableMultiplier.sol index b07f6c44..24e23cbf 100644 --- a/src/interfaces/multipliers/IProveableMultiplier.sol +++ b/src/interfaces/multipliers/IProveableMultiplier.sol @@ -2,7 +2,9 @@ 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 { diff --git a/src/multipliers/NFTMultiplier.sol b/src/multipliers/NFTMultiplier.sol index f0cb5798..536782ea 100644 --- a/src/multipliers/NFTMultiplier.sol +++ b/src/multipliers/NFTMultiplier.sol @@ -2,10 +2,11 @@ pragma solidity ^0.8.22; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; -import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; + /// @title NFT Multiplier /// @notice Implementation of INFTMultiplier interface /// @dev Provides multiplying factors based on NFT ownership diff --git a/src/multipliers/PermanentNFTMultiplier.sol b/src/multipliers/PermanentNFTMultiplier.sol index 31b5450b..2d490e6d 100644 --- a/src/multipliers/PermanentNFTMultiplier.sol +++ b/src/multipliers/PermanentNFTMultiplier.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.22; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; + import {INFTMultiplier} from "src/interfaces/multipliers/INFTMultiplier.sol"; /// @title Permanent NFT Multiplier diff --git a/src/multipliers/VotingStreakMultiplier.sol b/src/multipliers/VotingStreakMultiplier.sol index 8b76d349..cc026a6c 100644 --- a/src/multipliers/VotingStreakMultiplier.sol +++ b/src/multipliers/VotingStreakMultiplier.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.22; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; + import {IMultiplier} from "src/interfaces/multipliers/IMultiplier.sol"; import {MultiplierConstants} from "src/libraries/MultiplierConstants.sol"; import {YieldDistributor} from "src/YieldDistributor.sol"; @@ -63,7 +64,7 @@ contract VotingStreakMultiplier is Initializable, OwnableUpgradeable, IMultiplie function getMultiplyingFactor(address user) public view override returns (uint256) { if ( yieldDistributor.accountLastVoted(user) - > yieldDistributor.lastClaimedBlockNumber() - yieldDistributor.cycleLength() + > yieldDistributor.lastClaimedBlockNumber() - yieldDistributor.cycleLength() && block.number <= userToValidUntil[user] ) { return userToMultiplier[user]; diff --git a/test/ButteredBread.t.sol b/test/ButteredBread.t.sol index 3f46ea23..797ced94 100644 --- a/test/ButteredBread.t.sol +++ b/test/ButteredBread.t.sol @@ -8,15 +8,15 @@ import {Test} from "forge-std/Test.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; +import {IBread} from "bread-token/src/interfaces/IBread.sol"; import {ButteredBread, IButteredBread} from "src/ButteredBread.sol"; import {ICurveStableSwap} from "src/interfaces/ICurveStableSwap.sol"; import {IERC20Votes} from "src/interfaces/IERC20Votes.sol"; import {YieldDistributorTestWrapper} from "src/test/YieldDistributorTestWrapper.sol"; import {YieldDistributor, IYieldDistributor} from "src/YieldDistributor.sol"; -import {IBread} from "bread-token/src/interfaces/IBread.sol"; -import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {ERC20Mock} from "@openzeppelin/contracts/mocks/token/ERC20Mock.sol"; uint256 constant XDAI_FACTOR = 700; // 700% scaling factor; 7X uint256 constant TOKEN_AMOUNT = 1000 ether; diff --git a/test/upgrades/v1.0.0/YieldDistributor.sol b/test/upgrades/v1.0.0/YieldDistributor.sol index d42dc8f3..757f6f71 100644 --- a/test/upgrades/v1.0.0/YieldDistributor.sol +++ b/test/upgrades/v1.0.0/YieldDistributor.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.22; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import {Checkpoints} from - "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; import {Bread} from "bread-token/src/Bread.sol"; /** diff --git a/test/upgrades/v1.0.1/YieldDistributor.sol b/test/upgrades/v1.0.1/YieldDistributor.sol index 0f97adb4..b1db3efc 100644 --- a/test/upgrades/v1.0.1/YieldDistributor.sol +++ b/test/upgrades/v1.0.1/YieldDistributor.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.22; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import {Checkpoints} from - "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; import {Bread} from "bread-token/src/Bread.sol"; /** diff --git a/test/upgrades/v1.0.3/ButteredBread.sol b/test/upgrades/v1.0.3/ButteredBread.sol index 5d2502c4..cb6f5227 100644 --- a/test/upgrades/v1.0.3/ButteredBread.sol +++ b/test/upgrades/v1.0.3/ButteredBread.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.25 ^0.8.20 ^0.8.25; +pragma solidity ^0.8.20 ^0.8.25; // lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol diff --git a/test/upgrades/v1.0.4/ButteredBread.sol b/test/upgrades/v1.0.4/ButteredBread.sol index 04eff821..ee38ab52 100644 --- a/test/upgrades/v1.0.4/ButteredBread.sol +++ b/test/upgrades/v1.0.4/ButteredBread.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -pragma solidity =0.8.25 ^0.8.20 ^0.8.25; +pragma solidity ^0.8.20 ^0.8.25; // lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol @@ -775,10 +775,11 @@ library ECDSA { * - 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 recovered, RecoverError err, bytes32 errArg) { + function tryRecover(bytes32 hash, bytes memory signature) + internal + pure + returns (address recovered, RecoverError err, bytes32 errArg) + { if (signature.length == 65) { bytes32 r; bytes32 s; @@ -821,11 +822,11 @@ library ECDSA { * * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures] */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address recovered, RecoverError err, bytes32 errArg) { + function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) + internal + pure + returns (address recovered, RecoverError err, bytes32 errArg) + { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. @@ -847,12 +848,11 @@ library ECDSA { * @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 recovered, RecoverError err, bytes32 errArg) { + function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) + internal + pure + returns (address recovered, RecoverError err, bytes32 errArg) + { // 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 @@ -2156,11 +2156,10 @@ interface IButteredBread { * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { - function __Context_init() internal onlyInitializing { - } + function __Context_init() internal onlyInitializing {} + + function __Context_init_unchained() internal onlyInitializing {} - function __Context_init_unchained() internal onlyInitializing { - } function _msgSender() internal view virtual returns (address) { return msg.sender; } @@ -2201,11 +2200,10 @@ abstract contract NoncesUpgradeable is Initializable { } } - function __Nonces_init() internal onlyInitializing { - } + function __Nonces_init() internal onlyInitializing {} + + function __Nonces_init_unchained() internal onlyInitializing {} - function __Nonces_init_unchained() internal onlyInitializing { - } /** * @dev Returns the next unused nonce for an address. */ @@ -2284,7 +2282,8 @@ abstract contract ReentrancyGuardUpgradeable is Initializable { } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + bytes32 private constant ReentrancyGuardStorageLocation = + 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { @@ -2502,7 +2501,8 @@ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; + bytes32 private constant OwnableStorageLocation = + 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300; function _getOwnableStorage() private pure returns (OwnableStorage storage $) { assembly { @@ -3045,11 +3045,11 @@ library Math { /** * @dev Variant of {tryModExp} that supports inputs of arbitrary length. */ - function tryModExp( - bytes memory b, - bytes memory e, - bytes memory m - ) internal view returns (bool success, bytes memory result) { + function tryModExp(bytes memory b, bytes memory e, bytes memory m) + internal + view + returns (bool success, bytes memory result) + { if (_zeroBytes(m)) return (false, new bytes(0)); uint256 mLen = m.length; @@ -3375,7 +3375,8 @@ abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00; + bytes32 private constant Ownable2StepStorageLocation = + 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00; function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) { assembly { @@ -3385,11 +3386,10 @@ abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); - function __Ownable2Step_init() internal onlyInitializing { - } + function __Ownable2Step_init() internal onlyInitializing {} + + function __Ownable2Step_init_unchained() internal onlyInitializing {} - function __Ownable2Step_init_unchained() internal onlyInitializing { - } /** * @dev Returns the address of the pending owner. */ @@ -3467,11 +3467,10 @@ library Checkpoints { * 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 oldValue, uint224 newValue) { + function push(Trace224 storage self, uint32 key, uint224 value) + internal + returns (uint224 oldValue, uint224 newValue) + { return _insert(self._checkpoints, key, value); } @@ -3562,11 +3561,10 @@ library Checkpoints { * @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 oldValue, uint224 newValue) { + function _insert(Checkpoint224[] storage self, uint32 key, uint224 value) + private + returns (uint224 oldValue, uint224 newValue) + { uint256 pos = self.length; if (pos > 0) { @@ -3599,12 +3597,11 @@ library Checkpoints { * * 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) { + 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) { @@ -3623,12 +3620,11 @@ library Checkpoints { * * 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) { + 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) { @@ -3643,10 +3639,11 @@ library Checkpoints { /** * @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) { + 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) @@ -3670,11 +3667,10 @@ library Checkpoints { * 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 oldValue, uint208 newValue) { + function push(Trace208 storage self, uint48 key, uint208 value) + internal + returns (uint208 oldValue, uint208 newValue) + { return _insert(self._checkpoints, key, value); } @@ -3765,11 +3761,10 @@ library Checkpoints { * @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 oldValue, uint208 newValue) { + function _insert(Checkpoint208[] storage self, uint48 key, uint208 value) + private + returns (uint208 oldValue, uint208 newValue) + { uint256 pos = self.length; if (pos > 0) { @@ -3802,12 +3797,11 @@ library Checkpoints { * * 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) { + 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) { @@ -3826,12 +3820,11 @@ library Checkpoints { * * 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) { + 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) { @@ -3846,10 +3839,11 @@ library Checkpoints { /** * @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) { + 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) @@ -3873,11 +3867,10 @@ library Checkpoints { * 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 oldValue, uint160 newValue) { + function push(Trace160 storage self, uint96 key, uint160 value) + internal + returns (uint160 oldValue, uint160 newValue) + { return _insert(self._checkpoints, key, value); } @@ -3968,11 +3961,10 @@ library Checkpoints { * @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 oldValue, uint160 newValue) { + function _insert(Checkpoint160[] storage self, uint96 key, uint160 value) + private + returns (uint160 oldValue, uint160 newValue) + { uint256 pos = self.length; if (pos > 0) { @@ -4005,12 +3997,11 @@ library Checkpoints { * * 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) { + 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) { @@ -4029,12 +4020,11 @@ library Checkpoints { * * 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) { + 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) { @@ -4049,10 +4039,11 @@ library Checkpoints { /** * @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) { + 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) @@ -4129,10 +4120,11 @@ library Time { * @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 valueBefore, uint32 valueAfter, uint48 effect) { + function _getFullAt(Delay self, uint48 timepoint) + private + pure + returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) + { (valueBefore, valueAfter, effect) = self.unpack(); return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect); } @@ -4149,7 +4141,7 @@ library Time { * @dev Get the current value. */ function get(Delay self) internal view returns (uint32) { - (uint32 delay, , ) = self.getFull(); + (uint32 delay,,) = self.getFull(); return delay; } @@ -4158,11 +4150,11 @@ library Time { * 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) { + 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; @@ -4202,14 +4194,13 @@ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; - uint256 private constant SPECIAL_CHARS_LOOKUP = - (1 << 0x08) | // backspace - (1 << 0x09) | // tab - (1 << 0x0a) | // newline - (1 << 0x0c) | // form feed - (1 << 0x0d) | // carriage return - (1 << 0x22) | // double quote - (1 << 0x5c); // backslash + uint256 private constant SPECIAL_CHARS_LOOKUP = (1 << 0x08) // backspace + | (1 << 0x09) // tab + | (1 << 0x0a) // newline + | (1 << 0x0c) // form feed + | (1 << 0x0d) // carriage return + | (1 << 0x22) // double quote + | (1 << 0x5c); // backslash /** * @dev The `value` string doesn't fit in the specified `length`. @@ -4362,11 +4353,11 @@ library Strings { * * NOTE: This function will revert if the result does not fit in a `uint256`. */ - function tryParseUint( - string memory input, - uint256 begin, - uint256 end - ) internal pure returns (bool success, uint256 value) { + function tryParseUint(string memory input, uint256 begin, uint256 end) + internal + pure + returns (bool success, uint256 value) + { if (end > bytes(input).length || begin > end) return (false, 0); return _tryParseUintUncheckedBounds(input, begin, end); } @@ -4375,11 +4366,11 @@ library Strings { * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ - function _tryParseUintUncheckedBounds( - string memory input, - uint256 begin, - uint256 end - ) private pure returns (bool success, uint256 value) { + function _tryParseUintUncheckedBounds(string memory input, uint256 begin, uint256 end) + private + pure + returns (bool success, uint256 value) + { bytes memory buffer = bytes(input); uint256 result = 0; @@ -4435,11 +4426,11 @@ library Strings { * * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`. */ - function tryParseInt( - string memory input, - uint256 begin, - uint256 end - ) internal pure returns (bool success, int256 value) { + function tryParseInt(string memory input, uint256 begin, uint256 end) + internal + pure + returns (bool success, int256 value) + { if (end > bytes(input).length || begin > end) return (false, 0); return _tryParseIntUncheckedBounds(input, begin, end); } @@ -4448,11 +4439,11 @@ library Strings { * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ - function _tryParseIntUncheckedBounds( - string memory input, - uint256 begin, - uint256 end - ) private pure returns (bool success, int256 value) { + function _tryParseIntUncheckedBounds(string memory input, uint256 begin, uint256 end) + private + pure + returns (bool success, int256 value) + { bytes memory buffer = bytes(input); // Check presence of a negative sign. @@ -4467,7 +4458,9 @@ library Strings { return (true, negativeSign ? -int256(absValue) : int256(absValue)); } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) { return (true, type(int256).min); - } else return (false, 0); + } else { + return (false, 0); + } } /** @@ -4510,11 +4503,11 @@ library Strings { * * NOTE: This function will revert if the result does not fit in a `uint256`. */ - function tryParseHexUint( - string memory input, - uint256 begin, - uint256 end - ) internal pure returns (bool success, uint256 value) { + function tryParseHexUint(string memory input, uint256 begin, uint256 end) + internal + pure + returns (bool success, uint256 value) + { if (end > bytes(input).length || begin > end) return (false, 0); return _tryParseHexUintUncheckedBounds(input, begin, end); } @@ -4523,11 +4516,11 @@ library Strings { * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ - function _tryParseHexUintUncheckedBounds( - string memory input, - uint256 begin, - uint256 end - ) private pure returns (bool success, uint256 value) { + function _tryParseHexUintUncheckedBounds(string memory input, uint256 begin, uint256 end) + private + pure + returns (bool success, uint256 value) + { bytes memory buffer = bytes(input); // skip 0x prefix if present @@ -4583,11 +4576,11 @@ library Strings { * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly * formatted address. See {parseAddress-string-uint256-uint256} requirements. */ - function tryParseAddress( - string memory input, - uint256 begin, - uint256 end - ) internal pure returns (bool success, address value) { + function tryParseAddress(string memory input, uint256 begin, uint256 end) + internal + pure + returns (bool success, address value) + { if (end > bytes(input).length || begin > end) return (false, address(0)); bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty @@ -4635,13 +4628,19 @@ library Strings { bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i)); if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) { output[outputLength++] = "\\"; - if (char == 0x08) output[outputLength++] = "b"; - else if (char == 0x09) output[outputLength++] = "t"; - else if (char == 0x0a) output[outputLength++] = "n"; - else if (char == 0x0c) output[outputLength++] = "f"; - else if (char == 0x0d) output[outputLength++] = "r"; - else if (char == 0x5c) output[outputLength++] = "\\"; - else if (char == 0x22) { + if (char == 0x08) { + output[outputLength++] = "b"; + } else if (char == 0x09) { + output[outputLength++] = "t"; + } else if (char == 0x0a) { + output[outputLength++] = "n"; + } else if (char == 0x0c) { + output[outputLength++] = "f"; + } else if (char == 0x0d) { + output[outputLength++] = "r"; + } else if (char == 0x5c) { + output[outputLength++] = "\\"; + } else if (char == 0x22) { // solhint-disable-next-line quotes output[outputLength++] = '"'; } @@ -5062,18 +5061,19 @@ library MessageHashUtils { * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(hex"19_00", validator, data)); + return keccak256(abi.encodePacked(hex"1900", validator, data)); } /** * @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32. */ - function toDataWithIntendedValidatorHash( - address validator, - bytes32 messageHash - ) internal pure returns (bytes32 digest) { + function toDataWithIntendedValidatorHash(address validator, bytes32 messageHash) + internal + pure + returns (bytes32 digest) + { assembly ("memory-safe") { - mstore(0x00, hex"19_00") + mstore(0x00, hex"1900") mstore(0x02, shl(96, validator)) mstore(0x16, messageHash) digest := keccak256(0x00, 0x36) @@ -5092,7 +5092,7 @@ library MessageHashUtils { function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { assembly ("memory-safe") { let ptr := mload(0x40) - mstore(ptr, hex"19_01") + mstore(ptr, hex"1901") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) @@ -5228,15 +5228,16 @@ abstract contract EIP712Upgradeable is Initializable, IERC5267 { // 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) - ); + return + ( + hex"0f", // 01111 + _EIP712Name(), + _EIP712Version(), + block.chainid, + address(this), + bytes32(0), + new uint256[](0) + ); } /** @@ -5328,7 +5329,13 @@ abstract contract EIP712Upgradeable is Initializable, IERC5267 { * {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 { +abstract contract VotesUpgradeable is + Initializable, + ContextUpgradeable, + EIP712Upgradeable, + NoncesUpgradeable, + IERC5805 +{ using Checkpoints for Checkpoints.Trace208; bytes32 private constant DELEGATION_TYPEHASH = @@ -5362,11 +5369,10 @@ abstract contract VotesUpgradeable is Initializable, ContextUpgradeable, EIP712U */ error ERC5805FutureLookup(uint256 timepoint, uint48 clock); - function __Votes_init() internal onlyInitializing { - } + function __Votes_init() internal onlyInitializing {} + + function __Votes_init_unchained() 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. @@ -5461,22 +5467,15 @@ abstract contract VotesUpgradeable is Initializable, ContextUpgradeable, EIP712U /** * @dev Delegates votes from signer to `delegatee`. */ - function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { + 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 + _hashTypedDataV4(keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry))), v, r, s ); _useCheckedNonce(signer, nonce); _delegate(signer, delegatee); @@ -5518,19 +5517,13 @@ abstract contract VotesUpgradeable is Initializable, ContextUpgradeable, EIP712U VotesStorage storage $ = _getVotesStorage(); if (from != to && amount > 0) { if (from != address(0)) { - (uint256 oldValue, uint256 newValue) = _push( - $._delegateCheckpoints[from], - _subtract, - SafeCast.toUint208(amount) - ); + (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) - ); + (uint256 oldValue, uint256 newValue) = + _push($._delegateCheckpoints[to], _add, SafeCast.toUint208(amount)); emit DelegateVotesChanged(to, oldValue, newValue); } } @@ -5547,10 +5540,12 @@ abstract contract VotesUpgradeable is Initializable, ContextUpgradeable, EIP712U /** * @dev Get the `pos`-th checkpoint for `account`. */ - function _checkpoints( - address account, - uint32 pos - ) internal view virtual returns (Checkpoints.Checkpoint208 memory) { + function _checkpoints(address account, uint32 pos) + internal + view + virtual + returns (Checkpoints.Checkpoint208 memory) + { VotesStorage storage $ = _getVotesStorage(); return $._delegateCheckpoints[account].at(pos); } @@ -5600,11 +5595,10 @@ abstract contract ERC20VotesUpgradeable is Initializable, ERC20Upgradeable, Vote */ error ERC20ExceededSafeSupply(uint256 increasedSupply, uint256 cap); - function __ERC20Votes_init() internal onlyInitializing { - } + function __ERC20Votes_init() internal onlyInitializing {} + + function __ERC20Votes_init_unchained() internal onlyInitializing {} - function __ERC20Votes_init_unchained() internal onlyInitializing { - } /** * @dev Maximum token supply. Defaults to `type(uint208).max` (2^208^ - 1). * From af3a2e81da794e16ac42f7571c2d305d2cf461b7 Mon Sep 17 00:00:00 2001 From: bagelface Date: Thu, 13 Nov 2025 16:22:20 -0500 Subject: [PATCH 2/8] add in all the changes made in gk-breadchain --- src/YieldDistributor.sol | 170 +++++++++--- src/interfaces/IYieldDistributor.sol | 2 + src/test/YieldDistributorTestWrapper.sol | 4 +- test/YieldDistributor.t.sol | 337 +++++++++++++++++++++-- 4 files changed, 457 insertions(+), 56 deletions(-) diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index 0891b824..dc011d63 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -21,8 +21,11 @@ import {VotingMultipliers} from "src/VotingMultipliers.sol"; * @custom:coauthor prosalads.eth * @custom:coauthor kassandra.eth * @custom:coauthor theblockchainsocialist.eth + * @custom:coauthor cryptokibbutznik.eth * @custom:coauthor github.com/daopunk * @custom:coauthor github.com/secbajor + * @custom:coauthor github.com/hudsonhrh + * @custom:coauthor github.com/Tranquil-Flow */ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingMultipliers, GasKillerSDK { /// @notice The address of the $BREAD token contract @@ -37,7 +40,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM 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 + /// @notice The total voting power accumulated in the current cycle uint256 public currentVotes; /// @notice Array of projects eligible for yield distribution address[] public projects; @@ -57,6 +60,12 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM ERC20VotesUpgradeable public BUTTERED_BREAD; /// @notice The block number before the last yield distribution uint256 public previousCycleStartingBlock; + /// @notice Array of voters who have cast votes in the current cycle + address[] public voters; + /// @notice The mapping of holders to their vote distributions + mapping(address => uint256[]) public holderToDistribution; + /// @notice The mapping of holders to their total vote distribution + mapping(address => uint256) public holderToDistributionTotal; /// @custom:oz-upgrades-unsafe-allow constructor constructor() GasKillerSDK(address(0), address(0)) { @@ -190,12 +199,11 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM uint256 _available_yield = BREAD.balanceOf(address(this)) + BREAD.yieldAccrued(); if ( /// No votes were cast - /// Already claimed this cycle + /// OR already claimed this cycle + /// OR yield is insufficient 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)); @@ -203,38 +211,80 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM } /** - * @notice Distribute $BREAD yield to projects based on cast votes + * @notice Claims yield and prepares amounts for distribution + * @return _balance Total balance available for distribution + * @return _baseSplit Fixed split amount per project + * @return _votedYield Amount available for voted distribution */ - function distributeYield() public { + function _claimAndPrepareYield() internal returns (uint256 _balance, uint256 _baseSplit, uint256 _votedYield) { (bool _resolved,) = resolveYieldDistribution(); if (!_resolved) revert YieldNotResolved(); BREAD.claimYield(BREAD.yieldAccrued(), address(this)); previousCycleStartingBlock = lastClaimedBlockNumber; lastClaimedBlockNumber = block.number; - uint256 balance = BREAD.balanceOf(address(this)); - uint256 _fixedYield = balance / yieldFixedSplitDivisor; - uint256 _baseSplit = _fixedYield / projects.length; - uint256 _votedYield = balance - _fixedYield; + _balance = BREAD.balanceOf(address(this)); + uint256 _fixedYield = _balance / yieldFixedSplitDivisor; + _baseSplit = _fixedYield / projects.length; + _votedYield = _balance - _fixedYield; + } + + /** + * @notice Execute distribution to projects and finalize with cleanup + * @param distributions Array of voting power distributions per project + * @param totalVotes Total voting power + * @param balance Total balance being distributed + * @param _baseSplit Fixed split amount per project + * @param _votedYield Amount available for voted distribution + */ + function _executeAndFinalizeDistribution( + uint256[] memory distributions, + uint256 totalVotes, + uint256 balance, + uint256 _baseSplit, + uint256 _votedYield + ) internal { + // Execute transfers to projects for (uint256 i; i < projects.length; ++i) { - uint256 _votedSplit = ((projectDistributions[i] * _votedYield * PRECISION) / currentVotes) / PRECISION; - BREAD.transfer(projects[i], _votedSplit + _baseSplit); + uint256 _votedSplit = ((distributions[i] * _votedYield * PRECISION) / totalVotes) / PRECISION; + bool _success = BREAD.transfer(projects[i], _votedSplit + _baseSplit); + if (!_success) revert TransferFailed(); } + // Finalize with cleanup _updateBreadchainProjects(); + emit YieldDistributed(balance, totalVotes, distributions); - emit YieldDistributed(balance, currentVotes, projectDistributions); - + delete voters; delete currentVotes; projectDistributions = new uint256[](projects.length); } + /** + * @notice Distribute $BREAD yield to projects using GasKiller voting system + */ + function distributeYieldGK() public trackState { + (uint256 _balance, uint256 _baseSplit, uint256 _votedYield) = _claimAndPrepareYield(); + (uint256[] memory _currentProjectDistributions, uint256 _totalVotes) = _commitVotedDistribution(); + + _executeAndFinalizeDistribution(_currentProjectDistributions, _totalVotes, _balance, _baseSplit, _votedYield); + } + + /** + * @notice Distribute $BREAD yield to projects using gas efficient voting system to avoid OOG errors + */ + function distributeYield() public trackState { + (uint256 balance, uint256 _baseSplit, uint256 _votedYield) = _claimAndPrepareYield(); + + _executeAndFinalizeDistribution(projectDistributions, currentVotes, balance, _baseSplit, _votedYield); + } + /** * @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 { + function castVote(uint256[] calldata _points) public trackState { uint256 _currentVotingPower = getCurrentVotingPower(msg.sender); if (_currentVotingPower < minRequiredVotingPower) revert BelowMinRequiredVotingPower(); @@ -247,11 +297,15 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function castVoteWithMultipliers(uint256[] calldata _points, uint256[] calldata _multiplierIndices) + public + trackState + { uint256 _currentVotingPower = getCurrentVotingPower(msg.sender); - uint256 multiplier = calculateTotalMultipliers(msg.sender, _multiplierIndices); - _currentVotingPower = multiplier == 0 ? _currentVotingPower : (_currentVotingPower * multiplier) / PRECISION; + uint256 _multiplier = calculateTotalMultipliers(msg.sender, _multiplierIndices); + _currentVotingPower = _multiplier == 0 ? _currentVotingPower : (_currentVotingPower * _multiplier) / PRECISION; if (_currentVotingPower < minRequiredVotingPower) revert BelowMinRequiredVotingPower(); + _castVote(msg.sender, _points, _currentVotingPower); } @@ -262,29 +316,50 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 _projectsLength = projects.length; + if (_points.length != _projectsLength) revert IncorrectNumberOfProjects(); + + /// Add voter to list if they have not voted yet + if (holderToDistribution[_account].length > 0) { + delete holderToDistribution[_account]; + } else { + voters.push(_account); + } + holderToDistribution[_account] = _points; + /// Calculate total points uint256 _totalPoints; - for (uint256 i; i < _points.length; ++i) { + for (uint256 i; i < _projectsLength; ++i) { if (_points[i] > maxPoints) revert ExceedsMaxPoints(); _totalPoints += _points[i]; } if (_totalPoints == 0) revert ZeroVotePoints(); + holderToDistributionTotal[_account] = _totalPoints; bool _hasVotedInCycle = accountLastVoted[_account] > lastClaimedBlockNumber; uint256[] storage _voterDistributions = voterDistributions[_account]; if (!_hasVotedInCycle) { delete voterDistributions[_account]; currentVotes += _votingPower; + } else { + // When recasting, we need to subtract the old voting power and add the new one + uint256 _previousVotingPower; + for (uint256 i; i < _projectsLength; ++i) { + _previousVotingPower += _voterDistributions[i]; + } + currentVotes = currentVotes - _previousVotingPower + _votingPower; } - for (uint256 i; i < _points.length; ++i) { - if (!_hasVotedInCycle) _voterDistributions.push(0); - else projectDistributions[i] -= _voterDistributions[i]; - + for (uint256 i; i < _projectsLength; ++i) { uint256 _currentProjectDistribution = ((_points[i] * _votingPower * PRECISION) / _totalPoints) / PRECISION; - projectDistributions[i] += _currentProjectDistribution; - _voterDistributions[i] = _currentProjectDistribution; + if (!_hasVotedInCycle) { + projectDistributions[i] += _currentProjectDistribution; + _voterDistributions.push(_currentProjectDistribution); + } else { + // Update projectDistributions with the delta between new and old distributions + projectDistributions[i] = projectDistributions[i] - _voterDistributions[i] + _currentProjectDistribution; + _voterDistributions[i] = _currentProjectDistribution; + } } accountLastVoted[_account] = block.number; @@ -292,6 +367,33 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM emit BreadHolderVoted(_account, _points, projects); } + /** + * @notice Internal function for committing the voted distributions for projects + * @return _newProjectDistributions Distribution of votes for projects + * @return _totalVotes Total number of votes cast + */ + function _commitVotedDistribution() + internal + returns (uint256[] memory _newProjectDistributions, uint256 _totalVotes) + { + _newProjectDistributions = new uint256[](projects.length); + + for (uint256 i; i < voters.length; ++i) { + address _voter = voters[i]; + uint256 _voterPower = getCurrentVotingPower(_voter); + uint256[] memory _voterDistribution = holderToDistribution[_voter]; + uint256 _vote; + for (uint256 j; j < projects.length; ++j) { + _vote = + (_voterPower * _voterDistribution[j] * PRECISION / holderToDistributionTotal[_voter]) / PRECISION; + _newProjectDistributions[j] += _vote; + _totalVotes += _vote; + } + delete holderToDistribution[_voter]; + delete holderToDistributionTotal[_voter]; + } + } + /** * @notice Internal function for updating the project list */ @@ -332,7 +434,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function queueProjectAddition(address _project) public onlyOwner trackState { for (uint256 i; i < projects.length; ++i) { if (projects[i] == _project) { revert AlreadyMemberProject(); @@ -352,7 +454,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function queueProjectRemoval(address _project) public onlyOwner trackState { bool _found = false; for (uint256 i; i < projects.length; ++i) { if (projects[i] == _project) { @@ -375,7 +477,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function setMinRequiredVotingPower(uint256 _minRequiredVotingPower) public onlyOwner trackState { if (_minRequiredVotingPower == 0) revert MustBeGreaterThanZero(); minRequiredVotingPower = _minRequiredVotingPower; @@ -385,7 +487,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function setMaxPoints(uint256 _maxPoints) public onlyOwner trackState { if (_maxPoints == 0) revert MustBeGreaterThanZero(); maxPoints = _maxPoints; @@ -395,7 +497,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @notice Set a new cycle length in blocks * @param _cycleLength New cycle length in blocks */ - function setCycleLength(uint256 _cycleLength) public onlyOwner { + function setCycleLength(uint256 _cycleLength) public onlyOwner trackState { if (_cycleLength == 0) revert MustBeGreaterThanZero(); cycleLength = _cycleLength; @@ -405,7 +507,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @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 { + function setYieldFixedSplitDivisor(uint256 _yieldFixedSplitDivisor) public onlyOwner trackState { if (_yieldFixedSplitDivisor == 0) revert MustBeGreaterThanZero(); yieldFixedSplitDivisor = _yieldFixedSplitDivisor; @@ -415,7 +517,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @notice Set the ButteredBread token contract * @param _butteredBread Address of the ButteredBread token contract */ - function setButteredBread(address _butteredBread) public onlyOwner { + function setButteredBread(address _butteredBread) public onlyOwner trackState { BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); } @@ -424,7 +526,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @param newAvsAddress The new AVS address * @dev Also updates the namespace for the contract */ - function setAvsAddress(address newAvsAddress) external onlyOwner { + function setAvsAddress(address newAvsAddress) external onlyOwner trackState { _setAvsAddress(newAvsAddress); } } diff --git a/src/interfaces/IYieldDistributor.sol b/src/interfaces/IYieldDistributor.sol index aa3b91a6..f8398da1 100644 --- a/src/interfaces/IYieldDistributor.sol +++ b/src/interfaces/IYieldDistributor.sol @@ -27,6 +27,8 @@ interface IYieldDistributor { error YieldNotResolved(); /// @notice The error emitted if a user with zero points attempts to cast votes error ZeroVotePoints(); + /// @notice The error emitted when a transfer fails + error TransferFailed(); /// @notice The event emitted when an account casts a vote event BreadHolderVoted(address indexed account, uint256[] points, address[] projects); diff --git a/src/test/YieldDistributorTestWrapper.sol b/src/test/YieldDistributorTestWrapper.sol index cf7a8750..23abf772 100644 --- a/src/test/YieldDistributorTestWrapper.sol +++ b/src/test/YieldDistributorTestWrapper.sol @@ -15,8 +15,8 @@ contract YieldDistributorTestWrapper is YieldDistributor { } /** - * @notice Set the number of votes cast in the current cycle - * @param _currentVotes New number of votes cast in the current cycle + * @notice Set the total voting power in the current cycle + * @param _currentVotes New total voting power in the current cycle */ function setCurrentVotes(uint256 _currentVotes) public onlyOwner { currentVotes = _currentVotes; diff --git a/test/YieldDistributor.t.sol b/test/YieldDistributor.t.sol index 7bb57477..f7e685e1 100644 --- a/test/YieldDistributor.t.sol +++ b/test/YieldDistributor.t.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.13; import {Test, console2} from "forge-std/Test.sol"; import "forge-std/StdJson.sol"; -import {ERC20VotesUpgradeable} from - "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; +import { + ERC20VotesUpgradeable +} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; -import {TransparentUpgradeableProxy} from - "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; @@ -31,9 +31,10 @@ abstract contract Bread is ERC20VotesUpgradeable, Ownable2StepUpgradeable { contract YieldDistributorTest is Test { uint256 constant START = 32_323_232_323; - uint256 marginOfError = 3; + uint256 marginOfError = 3; // Optimized margin of error for rounding differences YieldDistributorTestWrapper public yieldDistributor; YieldDistributorTestWrapper public yieldDistributor2; + YieldDistributorTestWrapper public yieldDistributorGasKiller; address secondProject; uint256[] blockNumbers; uint256[] percentages; @@ -83,6 +84,7 @@ contract YieldDistributorTest is Test { yieldDistributor = YieldDistributorTestWrapper( address(new TransparentUpgradeableProxy(address(yieldDistributorImplementation), address(this), initData)) ); + secondProject = address(0x1234567890123456789012345678901234567890); address[] memory projects2 = new address[](2); projects2[0] = address(this); @@ -102,6 +104,26 @@ contract YieldDistributorTest is Test { yieldDistributor2 = YieldDistributorTestWrapper( address(new TransparentUpgradeableProxy(address(yieldDistributorImplementation), address(this), initData)) ); + + // Create a third instance for testing GasKiller distribution + address[] memory projects3 = new address[](1); + projects3[0] = address(this); + initData = abi.encodeWithSelector( + YieldDistributor.initialize.selector, + address(bread), + address(butteredBread), + _precision, + _minRequiredVotingPower, + _maxPoints, + _cycleLength, + _yieldFixedSplitDivisor, + _lastClaimedBlockNumber, + projects3 + ); + yieldDistributorGasKiller = YieldDistributorTestWrapper( + address(new TransparentUpgradeableProxy(address(yieldDistributorImplementation), address(this), initData)) + ); + address owner = bread.owner(); vm.prank(owner); bread.setYieldClaimer(address(yieldDistributor)); @@ -125,7 +147,7 @@ contract YieldDistributorTest is Test { } } - function test_simple_distribute() public { + function test_distributeYield_DistributesYieldToSingleProject() public { // Getting the balance of the project before the distribution uint256 bread_bal_before = bread.balanceOf(address(this)); assertEq(bread_bal_before, 0); @@ -141,7 +163,7 @@ contract YieldDistributorTest is Test { // Setting up for a cycle setUpForCycle(yieldDistributor); - // Casting vote and distributing yield + // Casting vote and distributing yield using legacy method uint256 vote = 100; percentages.push(vote); vm.prank(account); @@ -153,7 +175,7 @@ contract YieldDistributorTest is Test { assertGt(bread_bal_after, yieldAccrued - marginOfError); } - function test_fixed_yield_split() public { + function test_distributeYield_SplitsFixedAndVotedYieldCorrectly() public { // Getting the balance of the project before the distribution uint256 bread_bal_before = bread.balanceOf(address(this)); assertEq(bread_bal_before, 0); @@ -172,7 +194,7 @@ contract YieldDistributorTest is Test { vm.prank(owner); yieldDistributor2.setYieldFixedSplitDivisor(3); - // Casting vote and distributing yield + // Casting vote and distributing yield using legacy method uint256 vote = 50; uint256 vote2 = 50; percentages.push(vote); @@ -188,7 +210,7 @@ contract YieldDistributorTest is Test { assertGt(bread_bal_after, ((fixedSplit + votedSplit) / projectsLength) - marginOfError); } - function test_simple_recast_vote() public { + function test_castVote_AllowsRevotingInSamePeriod() public { // Getting the balance of the project before the distribution uint256 bread_bal_before = bread.balanceOf(address(this)); assertEq(bread_bal_before, 0); @@ -204,7 +226,7 @@ contract YieldDistributorTest is Test { // Setting up for a cycle setUpForCycle(yieldDistributor); - // Casting vote and distributing yield + // Casting vote and distributing yield using legacy method uint256 vote = 100; percentages.push(vote); vm.prank(account); @@ -220,7 +242,7 @@ contract YieldDistributorTest is Test { assertGt(bread_bal_after, yieldAccrued - marginOfError); } - function test_fuzzy_distribute(uint256 seed) public { + function test_distributeYield_HandlesMultipleVotersWithRandomDistributions(uint256 seed) public { // Getting the balance of the projects before the distribution uint256 breadbalproject1start = bread.balanceOf(address(this)); uint256 breadbalproject2start = bread.balanceOf(secondProject); @@ -253,7 +275,8 @@ contract YieldDistributorTest is Test { votes.pop(); votes.pop(); } - // Distributing yield + + // Distributing yield using legacy method yieldDistributor2.distributeYield(); // Getting the balance of the projects after the distribution @@ -263,7 +286,7 @@ contract YieldDistributorTest is Test { assertGt(second_bal_after, breadbalproject2start); } - function test_fuzzy_recast_vote(uint256 seed) public { + function test_castVote_HandlesMultipleRevotesWithFuzzing(uint256 seed) public { // Getting the balance of the projects before the distribution uint256 breadbalproject1start = bread.balanceOf(address(this)); uint256 breadbalproject2start = bread.balanceOf(secondProject); @@ -305,7 +328,8 @@ contract YieldDistributorTest is Test { votes.pop(); } vm.roll(START); - // Distributing yield + + // Distributing yield using legacy method yieldDistributor2.distributeYield(); // Getting the balance of the projects after the distribution @@ -315,13 +339,13 @@ contract YieldDistributorTest is Test { assertGt(second_bal_after, breadbalproject2start); } - function test_set_duration() public { + function test_setCycleLength_UpdatesCycleLength() public { yieldDistributor.setCycleLength(10); uint256 cycleLength = yieldDistributor.cycleLength(); assertEq(10, cycleLength); } - function test_voting_power() public { + function test_getVotingPowerForPeriod_CalculatesVotingPowerCorrectly() public { vm.roll(32_323_232_323); uint256 votingPowerBefore; vm.expectRevert(); @@ -353,7 +377,7 @@ contract YieldDistributorTest is Test { yieldDistributor.getVotingPowerForPeriod(bread, 42_424_242_424, 42_424_242_431, address(this)); } - function testFuzzy_voting_power(uint256 seed, uint256 mints) public { + function test_getVotingPowerForPeriod_MultipleMints(uint256 seed, uint256 mints) public { mints = uint256(bound(mints, 1, 100)); vm.assume(seed < 100_000_000_000 / mints); vm.assume(seed > 0); @@ -386,7 +410,7 @@ contract YieldDistributorTest is Test { assertEq(vote, expectedVotingPower); } - function test_adding_removing_projects() public { + function test_queueProjectAdditionAndRemoval_ManagesProjectList() public { // Checking to see if the project list length is initialized correctly vm.expectRevert(); address projects_before_len; @@ -434,7 +458,7 @@ contract YieldDistributorTest is Test { assertEq(length, 1); } - function test_below_min_required_voting_power() public { + function test_castVote_RevertsWhenBelowMinimumVotingPower() public { // Setting up an account without the minimum required voting power address account = address(0x1234567890123356789012345672901234567890); @@ -452,6 +476,160 @@ contract YieldDistributorTest is Test { vm.expectRevert(abi.encodeWithSelector(IYieldDistributor.BelowMinRequiredVotingPower.selector)); yieldDistributor.castVote(percentages); } + + // GasKiller Voting System Tests (default voting system) + + function test_distributeYieldGK_DistributesYieldToSingleProject() public { + // Getting the balance of the project before the distribution + uint256 bread_bal_before = bread.balanceOf(address(this)); + assertEq(bread_bal_before, 0); + // Getting the amount of yield to be distributed + uint256 yieldAccrued = bread.yieldAccrued(); + + // Setting up a voter + address account = address(0x1234567890123456789012345678901234567890); + address[] memory accounts = new address[](1); + accounts[0] = account; + setUpAccountsForVoting(accounts); + + // Setting up for a cycle + setUpForCycle(yieldDistributorGasKiller); + + // Casting vote and distributing yield using GasKiller method + uint256 vote = 100; + percentages.push(vote); + vm.prank(account); + yieldDistributorGasKiller.castVote(percentages); + yieldDistributorGasKiller.distributeYieldGK(); + + // Getting the balance of the project after the distribution and checking if it similar to the yield accrued (there may be rounding issues) + uint256 bread_bal_after = bread.balanceOf(address(this)); + assertGt(bread_bal_after, yieldAccrued - marginOfError); + } + + function test_distributeYieldGK_SplitsFixedAndVotedYieldCorrectly() public { + // Getting the balance of the project before the distribution + uint256 bread_bal_before = bread.balanceOf(address(this)); + assertEq(bread_bal_before, 0); + // Getting the amount of yield to be distributed + uint256 yieldAccrued = bread.yieldAccrued(); + + // Setting up a voter + address account = address(0x1234567890123456789012345678901234567890); + address[] memory accounts = new address[](1); + accounts[0] = account; + setUpAccountsForVoting(accounts); + + // Setting up for a cycle + setUpForCycle(yieldDistributorGasKiller); + address owner = yieldDistributorGasKiller.owner(); + vm.prank(owner); + yieldDistributorGasKiller.setYieldFixedSplitDivisor(3); + + // Casting vote and distributing yield using GasKiller method + // yieldDistributorGasKiller has 1 project, so we need 1 vote point + uint256 vote = 100; + percentages.push(vote); + vm.prank(account); + yieldDistributorGasKiller.castVote(percentages); + yieldDistributorGasKiller.distributeYieldGK(); + uint256 fixedSplit = yieldAccrued / _yieldFixedSplitDivisor; + uint256 votedSplit = yieldAccrued - fixedSplit; + uint256 projectsLength = yieldDistributorGasKiller.getProjectsLength(); + // Getting the balance of the project after the distribution and checking if it similar to the yield accrued (there may be rounding issues) + uint256 bread_bal_after = bread.balanceOf(address(this)); + assertGt(bread_bal_after, ((fixedSplit + votedSplit) / projectsLength) - marginOfError); + } + + function test_distributeYieldGK_HandlesMultipleVotersWithFuzzing(uint256 seed) public { + // Getting the balance of the project before the distribution + uint256 breadbalproject1start = bread.balanceOf(address(this)); + + // Generating random values for the test + vm.assume(seed > 10); + uint256 accounts = 3; + seed = uint256(bound(seed, 1, 100_000_000_000)); + + setUpForCycle(yieldDistributorGasKiller); + + for (uint256 i = 0; i < accounts; i++) { + // Generating random values for the test + uint256 randomval = uint256(keccak256(abi.encodePacked(seed, i))); + uint256 vote = bound(randomval, 1, 100); // Ensure vote is at least 1 to avoid ZeroVotePoints error + address holder = address(uint160(randomval)); + uint256 token_amount = bound(randomval, _minVotingAmount, 1000 * _minVotingAmount); + + // Setting up the account for voting + vm.roll(START - (minHoldingDurationInBlocks)); + vm.deal(holder, token_amount); + vm.prank(holder); + bread.mint{value: token_amount}(holder); + + // Casting vote with random distribution + // yieldDistributorGasKiller has 1 project, so we need 1 vote point + vm.roll(START); + votes.push(vote); + vm.prank(holder); + yieldDistributorGasKiller.castVote(votes); + votes.pop(); + } + // Distributing yield using GasKiller method + yieldDistributorGasKiller.distributeYieldGK(); + + // Getting the balance of the project after the distribution + uint256 this_bal_after = bread.balanceOf(address(this)); + assertGt(this_bal_after, breadbalproject1start); + } + + function test_distributeYieldGK_ClearsVoterDataAfterDistribution() public { + // Setting up multiple voters + address[] memory accounts = new address[](3); + accounts[0] = address(0x1); + accounts[1] = address(0x2); + accounts[2] = address(0x3); + setUpAccountsForVoting(accounts); + + // Setting up for a cycle + setUpForCycle(yieldDistributorGasKiller); + + // Each voter casts different votes + uint256[] memory votes1 = new uint256[](1); + votes1[0] = 100; + vm.prank(accounts[0]); + yieldDistributorGasKiller.castVote(votes1); + + uint256[] memory votes2 = new uint256[](1); + votes2[0] = 50; + vm.prank(accounts[1]); + yieldDistributorGasKiller.castVote(votes2); + + uint256[] memory votes3 = new uint256[](1); + votes3[0] = 25; + vm.prank(accounts[2]); + yieldDistributorGasKiller.castVote(votes3); + + // Check that all voters are recorded + assertEq(yieldDistributorGasKiller.voters(0), accounts[0]); + assertEq(yieldDistributorGasKiller.voters(1), accounts[1]); + assertEq(yieldDistributorGasKiller.voters(2), accounts[2]); + // Check that vote data is recorded correctly + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[0]), 100); + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[1]), 50); + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[2]), 25); + + // Distribute yield using GasKiller method + yieldDistributorGasKiller.distributeYieldGK(); + + // Check that voters array is cleared after distribution + vm.expectRevert(); + yieldDistributorGasKiller.voters(0); + + // Check that vote data is cleared after distribution + // After distribution, all totals should be reset to 0 + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[0]), 0); + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[1]), 0); + assertEq(yieldDistributorGasKiller.holderToDistributionTotal(accounts[2]), 0); + } } contract VotingStreakMultiplierTest is YieldDistributorTest { @@ -921,4 +1099,123 @@ contract VotingMultipliersTest is YieldDistributorTest { assertEq(address(yieldDistributor.allowlistedMultipliers(i)), address(multipliers[i])); } } + + /** + * @notice Test that revoting maintains correct total voting power + * @dev Ensures that when a user votes twice in the same period, + * the currentVotes total remains equal to their voting power + */ + function test_castVote_MaintainsCorrectTotalWhenRevoting() public { + // Setup voter with voting power + address voter = address(0xBEEF); + address[] memory accounts = new address[](1); + accounts[0] = voter; + setUpAccountsForVoting(accounts); + setUpForCycle(yieldDistributor2); + + // Get voter's voting power + uint256 voterPower = yieldDistributor2.getCurrentVotingPower(voter); + assertGt(voterPower, 0, "Voter should have voting power"); + + // First vote: 70% to project1, 30% to project2 + uint256[] memory votes1 = new uint256[](2); + votes1[0] = 7000; + votes1[1] = 3000; + vm.prank(voter); + yieldDistributor2.castVote(votes1); + + // Check currentVotes after first vote + uint256 totalAfterFirst = yieldDistributor2.currentVotes(); + assertEq(totalAfterFirst, voterPower, "Total should equal voter's power after first vote"); + + // Second vote (revote): 40% to project1, 60% to project2 + uint256[] memory votes2 = new uint256[](2); + votes2[0] = 4000; + votes2[1] = 6000; + vm.roll(block.number + 1); // Move forward slightly but stay in same period + vm.prank(voter); + yieldDistributor2.castVote(votes2); + + // Check currentVotes after revote - should still be the same + uint256 totalAfterRevote = yieldDistributor2.currentVotes(); + assertEq(totalAfterRevote, voterPower, "Total should still equal voter's power after revote"); + + // Verify project distributions are correctly updated + (, uint256[] memory distributions) = yieldDistributor2.getCurrentVotingDistribution(); + uint256 totalDistribution = distributions[0] + distributions[1]; + assertApproxEqRel(totalDistribution, voterPower, 1e15, "Total distribution should match voting power"); + } + + /** + * @notice Test multiple voters with revotes to ensure correct accumulation + */ + function test_castVote_AccumulatesMultipleVotersCorrectly() public { + address voter1 = address(0xBEEF); + address voter2 = address(0xCAFE); + address[] memory accounts = new address[](2); + accounts[0] = voter1; + accounts[1] = voter2; + setUpAccountsForVoting(accounts); + setUpForCycle(yieldDistributor2); + + uint256 voter1Power = yieldDistributor2.getCurrentVotingPower(voter1); + uint256 voter2Power = yieldDistributor2.getCurrentVotingPower(voter2); + + // Voter1 votes + uint256[] memory votes = new uint256[](2); + votes[0] = 5000; + votes[1] = 5000; + vm.prank(voter1); + yieldDistributor2.castVote(votes); + + // Voter2 votes + votes[0] = 3000; + votes[1] = 7000; + vm.prank(voter2); + yieldDistributor2.castVote(votes); + + // Check total is sum of both + uint256 totalAfterBoth = yieldDistributor2.currentVotes(); + assertEq(totalAfterBoth, voter1Power + voter2Power, "Total should be sum of both voters"); + + // Voter1 revotes + votes[0] = 8000; + votes[1] = 2000; + vm.prank(voter1); + yieldDistributor2.castVote(votes); + + // Total should still be the same + uint256 totalAfterRevote = yieldDistributor2.currentVotes(); + assertEq(totalAfterRevote, voter1Power + voter2Power, "Total should remain sum of both voters after revote"); + } + + function test_stateTransitionCount_IncrementsOnStateMutatingFunctions() public { + // Get initial state transition count + uint256 initialCount = yieldDistributor.stateTransitionCount(); + + // Call a state-mutating function (setCycleLength) + yieldDistributor.setCycleLength(200); + uint256 countAfterFirst = yieldDistributor.stateTransitionCount(); + assertEq(countAfterFirst, initialCount + 1, "State transition count should increment by 1"); + + // Call another state-mutating function (setMaxPoints) + yieldDistributor.setMaxPoints(10_000); + uint256 countAfterSecond = yieldDistributor.stateTransitionCount(); + assertEq(countAfterSecond, initialCount + 2, "State transition count should increment by 2"); + + // Call castVote (another state-mutating function) + address voter = address(0x123); + address[] memory voters = new address[](1); + voters[0] = voter; + setUpAccountsForVoting(voters); + setUpForCycle(yieldDistributor); + + uint256[] memory points = new uint256[](1); + points[0] = 100; + + vm.prank(voter); + yieldDistributor.castVote(points); + uint256 countAfterVote = yieldDistributor.stateTransitionCount(); + assertEq(countAfterVote, initialCount + 3, "State transition count should increment by 3"); + } } From 3ab14404b8ecf2ad52e81dfb3d4f32b682f05e03 Mon Sep 17 00:00:00 2001 From: bagelface Date: Thu, 13 Nov 2025 18:00:21 -0500 Subject: [PATCH 3/8] upgrade validation changes --- script/upgrades/ValidateUpgrade.s.sol | 2 +- src/VotingMultipliers.sol | 22 +- src/YieldDistributor.sol | 15 +- test/upgrades/v1.0.5/ButteredBread.sol | 4916 ++++++++++++++++++ test/upgrades/v1.0.5/YieldDistributor.sol | 5729 +++++++++++++++++++++ 5 files changed, 10671 insertions(+), 13 deletions(-) create mode 100644 test/upgrades/v1.0.5/ButteredBread.sol create mode 100644 test/upgrades/v1.0.5/YieldDistributor.sol diff --git a/script/upgrades/ValidateUpgrade.s.sol b/script/upgrades/ValidateUpgrade.s.sol index 45b58274..a2fdd453 100644 --- a/script/upgrades/ValidateUpgrade.s.sol +++ b/script/upgrades/ValidateUpgrade.s.sol @@ -9,7 +9,7 @@ contract DeployYieldDistributor is Script { function run() external { vm.startBroadcast(); Options memory opts; - opts.referenceContract = "v1.0.4/YieldDistributor.sol:YieldDistributor"; + opts.referenceContract = "v1.0.5/YieldDistributor.sol:YieldDistributor"; Upgrades.validateUpgrade("YieldDistributor.sol:YieldDistributor", opts); vm.stopBroadcast(); } diff --git a/src/VotingMultipliers.sol b/src/VotingMultipliers.sol index 25c8ef95..e29d16d8 100644 --- a/src/VotingMultipliers.sol +++ b/src/VotingMultipliers.sol @@ -17,19 +17,14 @@ contract VotingMultipliers is Ownable2StepUpgradeable, IVotingMultipliers { } // keccak256(abi.encode(uint256(keccak256("breadchain.VotingMultipliers.storage")) - 1)) & ~bytes32(uint256(0xff)); - bytes32 private constant VOTING_MULTIPLIERS_STORAGE_LOCATION = 0xf8ea84bd4d45550952f40e913fd59ad03bae30b4f3dc5a09695fefe1d0465d00; + bytes32 private constant VOTING_MULTIPLIERS_STORAGE_LOCATION = + 0xf8ea84bd4d45550952f40e913fd59ad03bae30b4f3dc5a09695fefe1d0465d00; /// @notice Initializes the contract - function initialize() public initializer { + function __VotingMultipliers_init() public onlyInitializing { __Ownable_init(msg.sender); } - function _getVotingMultipliersStorage() private pure returns (VotingMultipliersStorage storage $) { - assembly { - $.slot := VOTING_MULTIPLIERS_STORAGE_LOCATION - } - } - /// @notice Returns the multiplier at the given index /// @param index The index of the multiplier /// @return multiplier The multiplier at the given index @@ -156,4 +151,15 @@ contract VotingMultipliers is Ownable2StepUpgradeable, IVotingMultipliers { } return Math.max(_totalMultiplier, MultiplierConstants.BASE_MULTIPLIER); } + + /** + * @notice Returns a storage pointer to the VotingMultipliers storage struct + * @dev Internal/private pure helper to retrieve the VotingMultipliersStorage storage location + * @return $ VotingMultipliersStorage storage pointer + */ + function _getVotingMultipliersStorage() private pure returns (VotingMultipliersStorage storage $) { + assembly { + $.slot := VOTING_MULTIPLIERS_STORAGE_LOCATION + } + } } diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index dc011d63..01c21dcc 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -68,7 +68,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM mapping(address => uint256) public holderToDistributionTotal; /// @custom:oz-upgrades-unsafe-allow constructor - constructor() GasKillerSDK(address(0), address(0)) { + constructor() { _disableInitializers(); } @@ -83,8 +83,6 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM uint256 _lastClaimedBlockNumber, address[] memory _projects ) public initializer { - VotingMultipliers.initialize(); - if ( _bread == address(0) || _butteredBread == address(0) || _precision == 0 || _minRequiredVotingPower == 0 || _maxPoints == 0 || _cycleLength == 0 || _yieldFixedSplitDivisor == 0 || _lastClaimedBlockNumber == 0 @@ -109,12 +107,21 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM } } + /** + * @notice Initializes the VotingMultipliers contract + * @custom:oz-upgrades-validate-as-initializer + */ + function initializeVotingMultipliers() public reinitializer(1) { + __VotingMultipliers_init(); + } + /** * @notice Initializes the GasKiller SDK * @param _avsAddress The address of the AVS service manager * @param _blsSignatureChecker The address of the BLS signature checker + * @custom:oz-upgrades-validate-as-initializer */ - function initializeGasKiller(address _avsAddress, address _blsSignatureChecker) public reinitializer(1) { + function initializeGasKiller(address _avsAddress, address _blsSignatureChecker) public reinitializer(2) { _setAvsAddress(_avsAddress); _setBlsSignatureChecker(_blsSignatureChecker); } diff --git a/test/upgrades/v1.0.5/ButteredBread.sol b/test/upgrades/v1.0.5/ButteredBread.sol new file mode 100644 index 00000000..641f43d4 --- /dev/null +++ b/test/upgrades/v1.0.5/ButteredBread.sol @@ -0,0 +1,4916 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25 ^0.8.20 ^0.8.25; + +// 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); + } + } +} + +// src/interfaces/IButteredBread.sol + +/** + * @title `ButteredBread` interface + */ +interface IButteredBread { + /// @notice Occurs when a user does not have sufficient Butter to mint `ButteredBread` + error InsufficientFunds(); + /// @notice Occurs when an invalid value is attempted to be used in setter functions + error InvalidValue(); + /// @notice Occurs when attempting a deposit with a non-sanctioned LP + error NotAllowListed(); + /// @notice Occurs when attempting to delegate `ButteredBrea`d tokens. Delegations are set via the $BREAD contract + error NonDelegatable(); + /// @notice Occurs when attempting to transfer soulbound `ButteredBread` tokens + error NonTransferable(); + /// @notice Occurs when a dependent variable is not set + error UnsetVariable(); + /// @notice Occurs when a transfer fails + error TransferFailed(); + /// @notice Occurs when an amount is 0 + error AmountZero(); + + /// @notice The event emitted when an LP Token (Butter) has been added + event ButterAdded(address _account, address _lp, uint256 _amount); + /// @notice The event emitted when an LP Token (Butter) has been removed + event ButterRemoved(address _account, address _lp, uint256 _amount); + /// @notice Emitted whenever a scaling factor is updated for a sanctioned LP + event ScalingFactorModified(address indexed _lp, uint256 _previousFactor, uint256 _newFactor); + + /** + * @param breadToken Address of `BreadToken` + * @param liquidityPools Sanctioned LPs + * @param scalingFactors Scaling factor on mint per sanctioned LP + * @dev Each scaling factor is a fixed point percent (e.g. 100 = 1X, 150 = 1.5X, 1000 = 10X) + * @param name ERC20 token name + * @param symbol ERC20 token symbol + */ + struct InitData { + address breadToken; + address[] liquidityPools; + uint256[] scalingFactors; + string name; + string symbol; + } + + /** + * @param balance Value of deposited LP tokens (Butter) + * @param scalingFactor At the time of deposit or updated with `syncVotingWeight` function + */ + struct LPData { + uint256 balance; + uint256 scalingFactor; + } + + /// @notice Initialize contract as a `TransparentUpgradeableProxy` + function initialize(InitData calldata _initData) external; + + /// @notice Returns whether a given liquidity pool is Breadchain sanctioned or not + function allowlistedLPs(address _lp) external view returns (bool _allowed); + + /// @notice Returns the factor that determines how much `ButteredBread` should be minted for a Liquidity Pool token (Butter) + function scalingFactors(address _lp) external view returns (uint256 _factor); + + /// @notice Returns the amount of LP tokens (Butter) deposited for an account + function accountToLPBalance(address _account, address _lp) external view returns (uint256 _balance); + + /// @notice Deposits LP tokens (Butter) and mints `ButteredBread` according to the respective LP scaling factor + function deposit(address _lp, uint256 _amount) external; + + /// @notice Withdraws some amount of Butter (LP token) and burns an amount of the user's `ButteredBread` according to the respective scaling factor + function withdraw(address _lp, uint256 _amount) external; + + /// @notice Defines a liquidity pool's status as sanctioned or unsanctioned by Breadchain + function modifyAllowList(address _lp, bool _allowed) external; + + /// @notice Modifies how much `ButteredBread` should be minted for a Liquidity Pool token (Butter) + function modifyScalingFactor(address _lp, uint256 _factor, address[] calldata holders) external; +} + +// 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/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/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/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/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/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/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/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); + } + } +} + +// 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/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/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/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); +} + +// src/interfaces/IERC20Votes.sol + +interface IERC20Votes is IERC20 { + /** + * @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() external returns (uint48); + + /** + * @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; + + /** + * @dev Returns the current amount of votes that `account` has. + */ + function getVotes(address account) external 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 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. + */ + function getPastTotalSupply(uint256 timepoint) external returns (uint256); +} + +// 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/contracts/utils/ReentrancyGuardUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) + +/** + * @dev Contract module that helps prevent reentrant calls to a function. + * + * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier + * available, which can be applied to functions to make sure there are no nested + * (reentrant) calls to them. + * + * Note that because there is a single `nonReentrant` guard, functions marked as + * `nonReentrant` may not call one another. This can be worked around by making + * those functions `private`, and then adding `external` `nonReentrant` entry + * points to them. + * + * TIP: If you would like to learn more about reentrancy and alternative ways + * to protect against it, check out our blog post + * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. + */ +abstract contract ReentrancyGuardUpgradeable is Initializable { + // Booleans are more expensive than uint256 or any type that takes up a full + // word because each write operation emits an extra SLOAD to first read the + // slot's contents, replace the bits taken up by the boolean, and then write + // back. This is the compiler's defense against contract upgrades and + // pointer aliasing, and it cannot be disabled. + + // The values being non-zero value makes deployment a bit more expensive, + // but in exchange the refund on every call to nonReentrant will be lower in + // amount. Since refunds are capped to a percentage of the total + // transaction's gas, it is best to keep them low in cases like this one, to + // increase the likelihood of the full refund coming into effect. + uint256 private constant NOT_ENTERED = 1; + uint256 private constant ENTERED = 2; + + /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard + struct ReentrancyGuardStorage { + uint256 _status; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; + + function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { + assembly { + $.slot := ReentrancyGuardStorageLocation + } + } + + /** + * @dev Unauthorized reentrant call. + */ + error ReentrancyGuardReentrantCall(); + + function __ReentrancyGuard_init() internal onlyInitializing { + __ReentrancyGuard_init_unchained(); + } + + function __ReentrancyGuard_init_unchained() internal onlyInitializing { + ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); + $._status = NOT_ENTERED; + } + + /** + * @dev Prevents a contract from calling itself, directly or indirectly. + * Calling a `nonReentrant` function from another `nonReentrant` + * function is not supported. It is possible to prevent this from happening + * by making the `nonReentrant` function external, and making it call a + * `private` function that does the actual work. + */ + modifier nonReentrant() { + _nonReentrantBefore(); + _; + _nonReentrantAfter(); + } + + function _nonReentrantBefore() private { + ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); + // On the first call to nonReentrant, _status will be NOT_ENTERED + if ($._status == ENTERED) { + revert ReentrancyGuardReentrantCall(); + } + + // Any calls to nonReentrant after this point will fail + $._status = ENTERED; + } + + function _nonReentrantAfter() private { + ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); + // By storing the original value once again, a refund is triggered (see + // https://eips.ethereum.org/EIPS/eip-2200) + $._status = NOT_ENTERED; + } + + /** + * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a + * `nonReentrant` function in the call stack. + */ + function _reentrancyGuardEntered() internal view returns (bool) { + ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); + return $._status == ENTERED; + } +} + +// 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/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/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/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/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"19_00", 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"19_01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) + +/** + * @dev Contract module which provides access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is specified at deployment time in the constructor for `Ownable`. This + * can later be changed with {transferOwnership} and {acceptOwnership}. + * + * This module is used through inheritance. It will make available all functions + * from parent (Ownable). + */ +abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { + /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step + struct Ownable2StepStorage { + address _pendingOwner; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00; + + function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) { + assembly { + $.slot := Ownable2StepStorageLocation + } + } + + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + + function __Ownable2Step_init() internal onlyInitializing { + } + + function __Ownable2Step_init_unchained() internal onlyInitializing { + } + /** + * @dev Returns the address of the pending owner. + */ + function pendingOwner() public view virtual returns (address) { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + return $._pendingOwner; + } + + /** + * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual override onlyOwner { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + $._pendingOwner = newOwner; + emit OwnershipTransferStarted(owner(), newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual override { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + delete $._pendingOwner; + super._transferOwnership(newOwner); + } + + /** + * @dev The new owner accepts the ownership transfer. + */ + function acceptOwnership() public virtual { + address sender = _msgSender(); + if (pendingOwner() != sender) { + revert OwnableUnauthorizedAccount(sender); + } + _transferOwnership(sender); + } +} + +// 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); + } +} + +// src/ButteredBread.sol + +/** + * @title Breadchain Buttered Bread + * @notice Deposit LP tokens (Butter) to earn scaling rewards + * @author Breadchain Collective + * @custom:coauthor @RonTuretzky + * @custom:coauthor @daopunk + * @custom:coauthor @bagelface + */ +contract ButteredBread is IButteredBread, ERC20VotesUpgradeable, Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { + /// @notice Value used for calculating the precision of scaling factors + uint256 public constant FIXED_POINT_PERCENT = 100; + /// @notice `IERC20Votes` contract used for powering `ButteredBread` voting + IERC20Votes public bread; + /// @notice Access control for Breadchain sanctioned liquidity pools + mapping(address lp => bool allowed) public allowlistedLPs; + /// @notice How much ButteredBread should be minted for a Liquidity Pool token (Butter) + mapping(address lp => uint256 factor) public scalingFactors; + /// @notice Butter balance by account and Liquidity Pool token deposited + mapping(address account => mapping(address lp => LPData)) internal _accountToLPData; + + /// @dev Applied to functions to only allow access for sanctioned liquidity pools + modifier onlyAllowed(address _lp) { + if (!allowlistedLPs[_lp]) revert NotAllowListed(); + _; + } + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /// @param _initData See `IButteredBread` + function initialize(InitData calldata _initData) external initializer { + if (_initData.liquidityPools.length != _initData.scalingFactors.length) revert InvalidValue(); + bread = IERC20Votes(_initData.breadToken); + + __ERC20_init(_initData.name, _initData.symbol); + __EIP712_init(_initData.name, "1"); + __ERC20Votes_init(); + __Ownable_init(msg.sender); + __ReentrancyGuard_init(); + + for (uint256 i; i < _initData.liquidityPools.length; ++i) { + scalingFactors[_initData.liquidityPools[i]] = _initData.scalingFactors[i]; + allowlistedLPs[_initData.liquidityPools[i]] = true; + } + } + + /** + * @notice Return token balance of account for a specified LP + * @param _account Voting account + * @param _lp Liquidity Pool token + * @return _lpBalance Balance of LP tokens for an account by LP address + */ + function accountToLPBalance(address _account, address _lp) external view returns (uint256 _lpBalance) { + _lpBalance = _accountToLPData[_account][_lp].balance; + } + + /// @notice Sync this delegation with user delegate selection on $BREAD + function syncDelegation() external { + _syncDelegation(msg.sender); + } + + /** + * @notice Deposit LP tokens and mint ButteredBread + * NOTE: Additional ButteredBread can be minted/burned due to scaling factor changes + * @param _lp Liquidity Pool token + * @param _amount Value of LP token + */ + function deposit(address _lp, uint256 _amount) external onlyAllowed(_lp) nonReentrant { + if (_amount == 0) revert AmountZero(); + _deposit(msg.sender, _lp, _amount); + } + + /** + * @notice Withdraw LP tokens and burn ButteredBread + * NOTE: Additional ButteredBread can be minted/burned due to scaling factor changes + * @param _lp Liquidity Pool token + * @param _amount Value of LP token + */ + function withdraw(address _lp, uint256 _amount) external onlyAllowed(_lp) nonReentrant { + if (_amount == 0) revert AmountZero(); + _withdraw(msg.sender, _lp, _amount); + } + + /** + * @notice Allow or deny LP token + * @dev Must set scaling factor before sanctioning LP token + * @dev WARNING: When adding a new LP token, ensure that it has 18 decimals + * @param _lp Liquidity Pool token + * @param _allowed Sanction status of LP token + */ + function modifyAllowList(address _lp, bool _allowed) external onlyOwner { + if (scalingFactors[_lp] == 0) revert UnsetVariable(); + allowlistedLPs[_lp] = _allowed; + } + + /** + * @notice Set LP token scaling factor + * @param _lp Liquidity Pool token + * @param _factor Scaling percentage incentive of LP token (e.g. 100 = 1X, 150 = 1.5X, 1000 = 10X) + * @param _holders List of accounts to update with new scaling factor + */ + function modifyScalingFactor(address _lp, uint256 _factor, address[] calldata _holders) external onlyOwner { + _modifyScalingFactor(_lp, _factor, _holders); + } + + /// @notice `ButteredBread` tokens are non-transferable + function transfer(address, uint256) public virtual override returns (bool) { + revert NonTransferable(); + } + + /// @notice `ButteredBread` tokens are non-transferable + function transferFrom(address, address, uint256) public virtual override returns (bool) { + revert NonTransferable(); + } + + /// @notice `ButteredBread` delegation is determined by `BreadToken` + function delegate(address) public virtual override { + revert NonDelegatable(); + } + + /// @notice Get the LP data (balance and scaling factor) of a specific LP for a given account + /// @param _holder The address of the account to get the data for + /// @param _lp The address of the LP to get the data for + /// @return LPData memory The LP data containing balance and scaling factor for the given account + function getLPData(address _holder, address _lp) external view returns (LPData memory) { + return _accountToLPData[_holder][_lp]; + } + + /// @notice Deposit LP tokens and mint ButteredBread with corresponding LP scaling factor + function _deposit(address _account, address _lp, uint256 _amount) internal { + bool success = IERC20(_lp).transferFrom(_account, address(this), _amount); + if (!success) revert TransferFailed(); + + _syncDelegation(_account); + + /// @dev ensure proper accounting in case of admin error in `modifyScalingFactor` where not all holders are updated + _syncVotingWeight(_account, _lp); + _accountToLPData[_account][_lp].balance += _amount; + + _mint(_account, _amount * scalingFactors[_lp] / FIXED_POINT_PERCENT); + + emit ButterAdded(_account, _lp, _amount); + } + + /// @notice Withdraw LP tokens and burn ButteredBread with corresponding LP scaling factor + function _withdraw(address _account, address _lp, uint256 _amount) internal { + if (_amount > _accountToLPData[_account][_lp].balance) revert InsufficientFunds(); + _syncDelegation(_account); + + /// @dev ensure proper accounting in case of admin error in `modifyScalingFactor` where not all holders are updated + _syncVotingWeight(_account, _lp); + _accountToLPData[_account][_lp].balance -= _amount; + + _burn(_account, _amount * scalingFactors[_lp] / FIXED_POINT_PERCENT); + bool success = IERC20(_lp).transfer(_account, _amount); + if (!success) revert TransferFailed(); + + emit ButterRemoved(_account, _lp, _amount); + } + + function _modifyScalingFactor(address _lp, uint256 _factor, address[] calldata _holders) internal { + if (_factor < FIXED_POINT_PERCENT) revert InvalidValue(); + + uint256 old = scalingFactors[_lp]; + scalingFactors[_lp] = _factor; + emit ScalingFactorModified(_lp, old, _factor); + + for (uint256 i = 0; i < _holders.length; i++) { + _syncVotingWeight(_holders[i], _lp); + } + } + + /// @notice Sync this delegation with delegate selection on $BREAD + function _syncDelegation(address _account) internal { + _delegate(_account, bread.delegates(_account)); + if (this.delegates(_account) == address(0)) _delegate(_account, _account); + } + + /// @notice Sync voting weight with scaling factor + function _syncVotingWeight(address _account, address _lp) internal { + uint256 currentScalingFactor = scalingFactors[_lp]; + uint256 initialScalingFactor = _accountToLPData[_account][_lp].scalingFactor; + + if (currentScalingFactor != initialScalingFactor) { + uint256 lpBalance = _accountToLPData[_account][_lp].balance; + _accountToLPData[_account][_lp].scalingFactor = currentScalingFactor; + + if (lpBalance > 0) { + if (currentScalingFactor > initialScalingFactor) { + _mint( + _account, + (lpBalance * currentScalingFactor - lpBalance * initialScalingFactor) / FIXED_POINT_PERCENT + ); + } else { + _burn( + _account, + (lpBalance * initialScalingFactor - lpBalance * currentScalingFactor) / FIXED_POINT_PERCENT + ); + } + } + } + } +} + diff --git a/test/upgrades/v1.0.5/YieldDistributor.sol b/test/upgrades/v1.0.5/YieldDistributor.sol new file mode 100644 index 00000000..a0ba63c7 --- /dev/null +++ b/test/upgrades/v1.0.5/YieldDistributor.sol @@ -0,0 +1,5729 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0 ^0.8.20 ^0.8.22; + +// 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/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/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/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_0 { + /** + * @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/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_1 { + /** + * @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/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/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); +} + +// src/interfaces/multipliers/IMultiplier.sol + +interface IMultiplier { + /// @notice Updates the multiplying factor for a specific user + /// @param _user The address of the user to update the multiplying factor for + function updateMultiplyingFactor(address _user) external; + + /// @notice Returns the multiplying factor 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); +} + +// 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/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/bread-token-v2/src/interfaces/IWXDAI.sol + +interface IWXDAI { + function deposit() external payable; + function withdraw(uint256) external; +} + +// 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/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/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; + } +} + +// src/libraries/MultiplierConstants.sol + +library MultiplierConstants { + /// @notice The base multiplier value (100% in fixed-point representation) + uint256 public constant BASE_MULTIPLIER = 1e18; +} + +// 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/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); + } + } +} + +// 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/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/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/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_0 { + /** + * @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); +} + +// src/interfaces/IVotingMultipliers.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 Returns the array of allowlisted multipliers + /// @return IMultiplier[] The array of allowlisted multiplier contracts + function allowlistedMultipliers() external view returns (IMultiplier[] memory); + /// @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; +} + +// 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/contracts/interfaces/IERC5805.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5805.sol) + +interface IERC5805 is IERC6372, IVotes {} + +// 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/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/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/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"19_00", 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"19_01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} + +// lib/openzeppelin-contracts-upgradeable/contracts/access/Ownable2StepUpgradeable.sol + +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol) + +/** + * @dev Contract module which provides access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is specified at deployment time in the constructor for `Ownable`. This + * can later be changed with {transferOwnership} and {acceptOwnership}. + * + * This module is used through inheritance. It will make available all functions + * from parent (Ownable). + */ +abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable { + /// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step + struct Ownable2StepStorage { + address _pendingOwner; + } + + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00; + + function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) { + assembly { + $.slot := Ownable2StepStorageLocation + } + } + + event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); + + function __Ownable2Step_init() internal onlyInitializing { + } + + function __Ownable2Step_init_unchained() internal onlyInitializing { + } + /** + * @dev Returns the address of the pending owner. + */ + function pendingOwner() public view virtual returns (address) { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + return $._pendingOwner; + } + + /** + * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual override onlyOwner { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + $._pendingOwner = newOwner; + emit OwnershipTransferStarted(owner(), newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual override { + Ownable2StepStorage storage $ = _getOwnable2StepStorage(); + delete $._pendingOwner; + super._transferOwnership(newOwner); + } + + /** + * @dev The new owner accepts the ownership transfer. + */ + function acceptOwnership() public virtual { + address sender = _msgSender(); + if (pendingOwner() != sender) { + revert OwnableUnauthorizedAccount(sender); + } + _transferOwnership(sender); + } +} + +// 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_1 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_1 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_1 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_1 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_1 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_1 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_1 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/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_0, 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(""); + } + } + } +} + +// src/VotingMultipliers.sol + +/// @title VotingMultipliers +/// @notice A contract for managing voting multipliers +/// @dev Implements IVotingMultipliers interface +contract VotingMultipliers is Ownable2StepUpgradeable, IVotingMultipliers { + /// @custom:storage-location erc7201:breadchain.VotingMultipliers.storage + struct VotingMultipliersStorage { + IMultiplier[] allowlistedMultipliers; + } + + // keccak256(abi.encode(uint256(keccak256("breadchain.VotingMultipliers.storage")) - 1)) & ~bytes32(uint256(0xff)); + bytes32 private constant VOTING_MULTIPLIERS_STORAGE_LOCATION = 0xf8ea84bd4d45550952f40e913fd59ad03bae30b4f3dc5a09695fefe1d0465d00; + + /// @notice Initializes the contract + function initialize() public initializer { + __Ownable_init(msg.sender); + } + + function _getVotingMultipliersStorage() private pure returns (VotingMultipliersStorage storage $) { + assembly { + $.slot := VOTING_MULTIPLIERS_STORAGE_LOCATION + } + } + + /// @notice Returns the multiplier at the given index + /// @param index The index of the multiplier + /// @return multiplier The multiplier at the given index + function allowlistedMultipliers(uint256 index) external view returns (IMultiplier multiplier) { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + return $.allowlistedMultipliers[index]; + } + + /// @notice Returns the array of allowlisted multipliers + /// @return IMultiplier[] The array of allowlisted multipliers + function allowlistedMultipliers() external view returns (IMultiplier[] memory) { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + return $.allowlistedMultipliers; + } + + /// @notice Adds a multiplier to the allowlist + /// @param _multiplier The multiplier contract to be added + function addMultiplier(IMultiplier _multiplier) external onlyOwner { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + + // 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 { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + + 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) { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + + 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 + /// @notice Performs the updateMultiplyingFactor function for each multiplier to ensure the multiplier is up to date + /// @param _user The address of the user + /// @param _multiplierIndexes Array of multiplier indexes to use + /// @return The total multiplier value for the user + function calculateTotalMultipliers(address _user, uint256[] calldata _multiplierIndexes) public returns (uint256) { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + + uint256 _totalMultiplier = MultiplierConstants.BASE_MULTIPLIER; + + for (uint256 i = 0; i < _multiplierIndexes.length; i++) { + uint256 index = _multiplierIndexes[i]; + if (index >= $.allowlistedMultipliers.length) { + revert InvalidMultiplierIndex(); + } + + IMultiplier multiplier = $.allowlistedMultipliers[index]; + multiplier.updateMultiplyingFactor(_user); + if (block.number <= multiplier.validUntil(_user)) { + uint256 factor = multiplier.getMultiplyingFactor(_user); + if (factor > MultiplierConstants.BASE_MULTIPLIER) { + // Add only the bonus amount to the total + _totalMultiplier += (factor - MultiplierConstants.BASE_MULTIPLIER); + } + } + } + return Math.max(_totalMultiplier, MultiplierConstants.BASE_MULTIPLIER); + } + + /// @notice Calculates the total multiplier for a given user + /// @param _user The address of the _user + /// @return The total multiplier value for the _user + /// @dev This function is intended for frontend and testing purposes + function getTotalMultipliers(address _user) public view returns (uint256) { + VotingMultipliersStorage storage $ = _getVotingMultipliersStorage(); + + uint256 _totalMultiplier = MultiplierConstants.BASE_MULTIPLIER; + for (uint256 i = 0; i < $.allowlistedMultipliers.length; i++) { + IMultiplier multiplier = $.allowlistedMultipliers[i]; + if (block.number <= multiplier.validUntil(_user)) { + uint256 factor = multiplier.getMultiplyingFactor(_user); + if (factor > MultiplierConstants.BASE_MULTIPLIER) { + // Add only the bonus amount to the total + _totalMultiplier += (factor - MultiplierConstants.BASE_MULTIPLIER); + } + } + } + return Math.max(_totalMultiplier, MultiplierConstants.BASE_MULTIPLIER); + } +} + +// 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_1; + + 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_1(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_1(tok).safeTransfer(owner(), amount); + } + + function yieldAccrued() external view returns (uint256) { + return _yieldAccrued(); + } + + function _yieldAccrued() internal view returns (uint256) { + uint256 bal = IERC20_1(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 postcapitalistcrypto.eth + * @custom:coauthor bagelface.eth + * @custom:coauthor prosalads.eth + * @custom:coauthor kassandra.eth + * @custom:coauthor theblockchainsocialist.eth + * @custom:coauthor github.com/daopunk + * @custom:coauthor github.com/secbajor + */ +contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingMultipliers { + /// @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 each project 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 each project by a specific voter 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; + /// @notice The block number before the last yield distribution + uint256 public previousCycleStartingBlock; + + /// @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 { + VotingMultipliers.initialize(); + 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, previousCycleStartingBlock, lastClaimedBlockNumber, _account) + + this.getVotingPowerForPeriod(BUTTERED_BREAD, previousCycleStartingBlock, lastClaimedBlockNumber, _account); + } + + /// @notice Get the current accumulated voting power for a user + /// @dev This is the voting power that has been accumulated since the last yield distribution + /// @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); + } + + /** + * @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 + ) public 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 _numCheckpoints = _sourceContract.numCheckpoints(_account); + if (_numCheckpoints == 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; + + uint256 _totalVotingPower; + + for (uint32 i = _numCheckpoints; i > 0;) { + _currentCheckpoint = _sourceContract.checkpoints(_account, --i); + + if (_currentCheckpoint._key <= _end) { + uint48 _effectiveStart = _currentCheckpoint._key < _start ? uint48(_start) : _currentCheckpoint._key; + _totalVotingPower += _currentCheckpoint._value * (_end - _effectiveStart); + + if (_effectiveStart == _start) break; + _end = _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)); + previousCycleStartingBlock = lastClaimedBlockNumber; + 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 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); + uint256 multiplier = calculateTotalMultipliers(msg.sender, _multiplierIndices); + _currentVotingPower = multiplier == 0 ? _currentVotingPower : (_currentVotingPower * multiplier) / PRECISION; + 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); + } +} + From 861990156100977a652af326755582159fdc709a Mon Sep 17 00:00:00 2001 From: bagelface Date: Thu, 13 Nov 2025 18:22:52 -0500 Subject: [PATCH 4/8] add reinitializers --- lib/gas-killer-solidity-sdk | 2 +- src/VotingMultipliers.sol | 5 +++-- src/YieldDistributor.sol | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/gas-killer-solidity-sdk b/lib/gas-killer-solidity-sdk index 89762cdb..0809cc55 160000 --- a/lib/gas-killer-solidity-sdk +++ b/lib/gas-killer-solidity-sdk @@ -1 +1 @@ -Subproject commit 89762cdb6992d1ff29bdbe36dad60f14026519a3 +Subproject commit 0809cc550d039cdcab07c97f36156907fa4db100 diff --git a/src/VotingMultipliers.sol b/src/VotingMultipliers.sol index e29d16d8..026beecf 100644 --- a/src/VotingMultipliers.sol +++ b/src/VotingMultipliers.sol @@ -21,8 +21,9 @@ contract VotingMultipliers is Ownable2StepUpgradeable, IVotingMultipliers { 0xf8ea84bd4d45550952f40e913fd59ad03bae30b4f3dc5a09695fefe1d0465d00; /// @notice Initializes the contract - function __VotingMultipliers_init() public onlyInitializing { - __Ownable_init(msg.sender); + /// @param _initialOwner The address of the initial owner + function __VotingMultipliers_init(address _initialOwner) public onlyInitializing { + __Ownable_init(_initialOwner); } /// @notice Returns the multiplier at the given index diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index 01c21dcc..f4236941 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -109,10 +109,11 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM /** * @notice Initializes the VotingMultipliers contract + * @param _initialOwner The address of the initial owner * @custom:oz-upgrades-validate-as-initializer */ - function initializeVotingMultipliers() public reinitializer(1) { - __VotingMultipliers_init(); + function initializeVotingMultipliers(address _initialOwner) public reinitializer(1) { + __VotingMultipliers_init(_initialOwner); } /** From 4cacbb1bd84a347221c81dc0359043523b2349c7 Mon Sep 17 00:00:00 2001 From: bagelface Date: Thu, 13 Nov 2025 18:28:50 -0500 Subject: [PATCH 5/8] update gas-killer-solidity-sdk submodule --- lib/gas-killer-solidity-sdk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gas-killer-solidity-sdk b/lib/gas-killer-solidity-sdk index 0809cc55..ef421e27 160000 --- a/lib/gas-killer-solidity-sdk +++ b/lib/gas-killer-solidity-sdk @@ -1 +1 @@ -Subproject commit 0809cc550d039cdcab07c97f36156907fa4db100 +Subproject commit ef421e278750266882ea4e16cff5267416c94ac6 From 136d2a1a0d90ad2432c866b6817c0adc8f54e1c5 Mon Sep 17 00:00:00 2001 From: bagelface Date: Fri, 14 Nov 2025 14:28:48 -0500 Subject: [PATCH 6/8] feat: enable optimizer and use interfaces for smaller YieldDistributor contract size --- foundry.toml | 2 ++ script/deploy/DeployYieldDistributorImpl.s.sol | 16 ++++++++++++++++ src/VotingMultipliers.sol | 2 +- src/YieldDistributor.sol | 18 ++++++++---------- src/interfaces/IBread.sol | 8 ++++++++ src/interfaces/IERC20Votes.sol | 11 +++++++++++ test/YieldDistributor.t.sol | 6 ++---- 7 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 script/deploy/DeployYieldDistributorImpl.s.sol create mode 100644 src/interfaces/IBread.sol diff --git a/foundry.toml b/foundry.toml index c72ab833..20bb430b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -14,6 +14,8 @@ ffi = true ast = true extra_output = ["storageLayout"] evm_version = "cancun" # Compile Curve LP interface +optimizer = true +optimizer_runs = 10000 [rpc_endpoints] sepolia = "QUICKNODE_ENDPOINT_URL" diff --git a/script/deploy/DeployYieldDistributorImpl.s.sol b/script/deploy/DeployYieldDistributorImpl.s.sol new file mode 100644 index 00000000..cce3908d --- /dev/null +++ b/script/deploy/DeployYieldDistributorImpl.s.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.20; + +import "forge-std/Script.sol"; +import "forge-std/StdJson.sol"; +import "forge-std/console.sol"; + +import {YieldDistributor} from "../../src/YieldDistributor.sol"; + +contract DeployYieldDistributorImpl is Script { + function run() external { + vm.startBroadcast(); + YieldDistributor yieldDistributor = new YieldDistributor(); + console2.log("Deployed YieldDistributor at address: {}", address(yieldDistributor)); + vm.stopBroadcast(); + } +} diff --git a/src/VotingMultipliers.sol b/src/VotingMultipliers.sol index 026beecf..30054719 100644 --- a/src/VotingMultipliers.sol +++ b/src/VotingMultipliers.sol @@ -22,7 +22,7 @@ contract VotingMultipliers is Ownable2StepUpgradeable, IVotingMultipliers { /// @notice Initializes the contract /// @param _initialOwner The address of the initial owner - function __VotingMultipliers_init(address _initialOwner) public onlyInitializing { + function __VotingMultipliers_init(address _initialOwner) internal onlyInitializing { __Ownable_init(_initialOwner); } diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index f4236941..2b2bc6db 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -3,13 +3,11 @@ pragma solidity ^0.8.22; import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; -import { - ERC20VotesUpgradeable -} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; import {GasKillerSDK} from "gas-killer/flat/GasKillerSDK.flat.sol"; -import {Bread} from "bread-token/src/Bread.sol"; import {IYieldDistributor} from "src/interfaces/IYieldDistributor.sol"; +import {IBread} from "src/interfaces/IBread.sol"; +import {IERC20Votes} from "src/interfaces/IERC20Votes.sol"; import {VotingMultipliers} from "src/VotingMultipliers.sol"; /** @@ -29,7 +27,7 @@ import {VotingMultipliers} from "src/VotingMultipliers.sol"; */ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingMultipliers, GasKillerSDK { /// @notice The address of the $BREAD token contract - Bread public BREAD; + IBread public BREAD; /// @notice The precision to use for calculations uint256 public PRECISION; /// @notice The minimum number of blocks between yield distributions @@ -57,7 +55,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM /// @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; + IERC20Votes public BUTTERED_BREAD; /// @notice The block number before the last yield distribution uint256 public previousCycleStartingBlock; /// @notice Array of voters who have cast votes in the current cycle @@ -91,8 +89,8 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM revert MustBeGreaterThanZero(); } - BREAD = Bread(_bread); - BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); + BREAD = IBread(_bread); + BUTTERED_BREAD = IERC20Votes(_butteredBread); PRECISION = _precision; minRequiredVotingPower = _minRequiredVotingPower; maxPoints = _maxPoints; @@ -163,7 +161,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @return uint256 Voting power of the specified user at the specified period of time */ function getVotingPowerForPeriod( - ERC20VotesUpgradeable _sourceContract, + IERC20Votes _sourceContract, uint256 _start, uint256 _end, address _account @@ -526,7 +524,7 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM * @param _butteredBread Address of the ButteredBread token contract */ function setButteredBread(address _butteredBread) public onlyOwner trackState { - BUTTERED_BREAD = ERC20VotesUpgradeable(_butteredBread); + BUTTERED_BREAD = IERC20Votes(_butteredBread); } /** diff --git a/src/interfaces/IBread.sol b/src/interfaces/IBread.sol new file mode 100644 index 00000000..76eb0527 --- /dev/null +++ b/src/interfaces/IBread.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.25; + +import {IBread as IBreadSource} from "bread-token/src/interfaces/IBread.sol"; + +import {IERC20Votes} from "src/interfaces/IERC20Votes.sol"; + +interface IBread is IBreadSource, IERC20Votes {} diff --git a/src/interfaces/IERC20Votes.sol b/src/interfaces/IERC20Votes.sol index 23b63995..d05a10e6 100644 --- a/src/interfaces/IERC20Votes.sol +++ b/src/interfaces/IERC20Votes.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.25; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {Checkpoints} from "@openzeppelin/contracts/utils/structs/Checkpoints.sol"; interface IERC20Votes is IERC20 { /** @@ -41,4 +42,14 @@ interface IERC20Votes is IERC20 { * configured to use block numbers, this will return the value at the end of the corresponding block. */ function getPastTotalSupply(uint256 timepoint) external returns (uint256); + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) external view returns (uint32); + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) external view returns (Checkpoints.Checkpoint208 memory); } diff --git a/test/YieldDistributor.t.sol b/test/YieldDistributor.t.sol index f7e685e1..23f7a3f0 100644 --- a/test/YieldDistributor.t.sol +++ b/test/YieldDistributor.t.sol @@ -19,13 +19,11 @@ import {VotingMultipliers, IVotingMultipliers} from "src/VotingMultipliers.sol"; import {VotingStreakMultiplier} from "src/multipliers/VotingStreakMultiplier.sol"; import {MockMultiplier} from "src/test/MockMultiplier.sol"; import {IMultiplier} from "src/interfaces/IVotingMultipliers.sol"; +import {IBread} from "src/interfaces/IBread.sol"; import {NFTMultiplier} from "src/multipliers/NFTMultiplier.sol"; import {DeployNFTMultiplier} from "script/deploy/DeployNFTMultiplier.s.sol"; -abstract contract Bread is ERC20VotesUpgradeable, Ownable2StepUpgradeable { - function claimYield(uint256 amount, address receiver) public virtual; - function yieldAccrued() external view virtual returns (uint256); - function setYieldClaimer(address _yieldClaimer) external virtual; +abstract contract Bread is IBread, Ownable2StepUpgradeable { function mint(address receiver) external payable virtual; } From 043d5b214fef4023b930d278c2080df705df8123 Mon Sep 17 00:00:00 2001 From: RonTuretzky <74178515+RonTuretzky@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:15:25 -0500 Subject: [PATCH 7/8] Update src/YieldDistributor.sol --- src/YieldDistributor.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index 2b2bc6db..3ebef545 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -19,7 +19,7 @@ import {VotingMultipliers} from "src/VotingMultipliers.sol"; * @custom:coauthor prosalads.eth * @custom:coauthor kassandra.eth * @custom:coauthor theblockchainsocialist.eth - * @custom:coauthor cryptokibbutznik.eth + * @custom:coauthor postcapitalistcrypto.eth * @custom:coauthor github.com/daopunk * @custom:coauthor github.com/secbajor * @custom:coauthor github.com/hudsonhrh From 39ff8efa5783b3757c2dcdc5469524bdd69f5d63 Mon Sep 17 00:00:00 2001 From: bagelface Date: Wed, 19 Nov 2025 17:04:59 -0500 Subject: [PATCH 8/8] feat: add function to set BLS signature checker contract --- src/YieldDistributor.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/YieldDistributor.sol b/src/YieldDistributor.sol index 3ebef545..5682522c 100644 --- a/src/YieldDistributor.sol +++ b/src/YieldDistributor.sol @@ -535,4 +535,12 @@ contract YieldDistributor is IYieldDistributor, Ownable2StepUpgradeable, VotingM function setAvsAddress(address newAvsAddress) external onlyOwner trackState { _setAvsAddress(newAvsAddress); } + + /** + * @notice Allows the owner to set the BLS signature checker address + * @param newBlsSignatureChecker The new BLS signature checker address + */ + function setBlsSignatureChecker(address newBlsSignatureChecker) external onlyOwner trackState { + _setBlsSignatureChecker(newBlsSignatureChecker); + } }