Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/rewards/StakingRewards.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {ZKC} from "../ZKC.sol";
import {IZKC} from "../interfaces/IZKC.sol";
import {IRewards} from "../interfaces/IRewards.sol";
/// @notice Error thrown when a user tries to claim rewards for an epoch they have already claimed
error AlreadyClaimed(uint256 epoch);
/// @title StakingRewards
/// @notice Contract for distributing staking rewards based on veZKC staking positions
/// @dev Users can claim rewards for specific epochs based on their staking value
contract StakingRewards is Initializable, AccessControlUpgradeable, UUPSUpgradeable, ReentrancyGuardUpgradeable {
bytes32 public constant ADMIN_ROLE = DEFAULT_ADMIN_ROLE;
/// @notice ZKC token contract
ZKC public zkc;
/// @notice veZKC rewards interface for getting staking positions
IRewards public veZKC;
/// @dev Mapping to track if a user has claimed rewards for an epoch
mapping(uint256 epoch => mapping(address user => bool claimed)) private _userClaimed;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Initialize the StakingRewards contract
/// @dev Sets up the contract with ZKC token, veZKC rewards interface, and admin role
/// @param _zkc Address of the ZKC token contract
/// @param _veZKC Address of the veZKC contract implementing IRewards
/// @param _admin Address that will be granted the admin role
function initialize(address _zkc, address _veZKC, address _admin) public initializer {
__AccessControl_init();
__UUPSUpgradeable_init();
__ReentrancyGuard_init();
require(_zkc != address(0), "ZKC cannot be zero address");
require(_veZKC != address(0), "veZKC cannot be zero address");
require(_admin != address(0), "Admin cannot be zero address");
zkc = ZKC(_zkc);
veZKC = IRewards(_veZKC);
_grantRole(ADMIN_ROLE, _admin);
}
/// @notice Claim rewards for the given epochs
/// @param epochs The epochs to claim rewards for
/// @return amount The amount of rewards claimed
function claimRewards(uint256[] calldata epochs) external nonReentrant returns (uint256) {
return _claim(msg.sender, epochs, msg.sender);
}
/// @notice Claim rewards for the given epochs and send to a recipient
/// @param epochs The epochs to claim rewards for
/// @param recipient The address to receive the minted rewards
/// @return amount The amount of rewards claimed
function claimRewardsToRecipient(uint256[] calldata epochs, address recipient)
external
nonReentrant
returns (uint256)
{
require(recipient != address(0), "Recipient cannot be zero address");
return _claim(msg.sender, epochs, recipient);
}
/// @notice Calculate the rewards a user is owed for the given epochs. If the epoch has not ended yet, it will return zero rewards.
/// @dev Unlike claimRewards(), this allows duplicate epochs and current/future epochs.
/// Callers should validate epochs before calling this function.
/// Duplicate epochs will return the same reward amount.
/// Current/future epochs will return zero rewards (as those epochs have not ended yet)
/// @param user The user address
/// @param epochs The epochs to calculate rewards for
/// @return rewards The rewards owed
function calculateRewards(address user, uint256[] calldata epochs) external returns (uint256[] memory) {
return _calculate(user, epochs);
}
/// @notice Calculate unclaimed rewards for a user - returns 0 for already claimed epochs
/// @dev This function is gas inefficient. It should only be called off-chain as part of view function calls.
/// @param user The user address
/// @param epochs The epochs to calculate unclaimed rewards for
/// @return rewards The unclaimed rewards (0 if already claimed)
function calculateUnclaimedRewards(address user, uint256[] calldata epochs) external returns (uint256[] memory) {
uint256[] memory rewards = _calculate(user, epochs);
for (uint256 i = 0; i < epochs.length; i++) {
if (_userClaimed[epochs[i]][user]) {
rewards[i] = 0;
}
}
return rewards;
}
/// @notice Check if a user has claimed rewards for a specific epoch
/// @param user The user address
/// @param epoch The epoch to check
/// @return claimed Whether rewards have been claimed
function hasUserClaimedRewards(address user, uint256 epoch) external view returns (bool) {
return _userClaimed[epoch][user];
}
/// @notice Get the current epoch from the ZKC contract
/// @return currentEpoch The current epoch number
function getCurrentEpoch() external view returns (uint256) {
return zkc.getCurrentEpoch();
}
/// @notice Get estimated pending rewards for the current epoch
/// @dev This is an estimate only and may change by the end of the epoch as staking positions change
/// @param user The user address to check pending rewards for
/// @return pendingRewards The estimated rewards for the current epoch
function getPendingRewards(address user) external returns (uint256) {
uint256 currentEpoch = zkc.getCurrentEpoch();
// Get current staking power for the user
uint256 userPower = veZKC.getStakingRewards(user);
if (userPower == 0) return 0;
// Get total staking power
uint256 totalPower = veZKC.getTotalStakingRewards();
if (totalPower == 0) return 0;
// Get emission for current epoch
uint256 emission = zkc.getStakingEmissionsForEpoch(currentEpoch);
// Calculate proportional share
return (emission * userPower) / totalPower;
}
/// @notice Get the end timestamp for a specific epoch
/// @param epoch The epoch number
/// @return endTimestamp The end timestamp of the epoch
function _epochEndTimestamp(uint256 epoch) internal view returns (uint256) {
return zkc.getEpochEndTime(epoch);
}
/// @notice Internal function to calculate the rewards a user is owed for the given epochs
/// @dev Unlike _claim(), this allows duplicate epochs and current/future epochs.
/// Callers should validate epochs when using this function.
/// Duplicate epochs will return the same reward amount.
/// Current/future epochs will return zero rewards (as those epochs have not ended yet)
/// @param user The user address
/// @param epochs The epochs to calculate rewards for
/// @return rewards The list of rewards owed
function _calculate(address user, uint256[] calldata epochs) internal returns (uint256[] memory) {
ZKC zkcMemory = zkc;
IRewards veZKCMemory = veZKC;
uint256 currentEpoch = zkcMemory.getCurrentEpoch();
uint256[] memory rewards = new uint256[](epochs.length);
for (uint256 i; i < epochs.length; ++i) {
uint256 epoch = epochs[i];
if (epoch >= currentEpoch) continue; // cannot claim ongoing/future epoch
uint256 snapshotTime = _epochEndTimestamp(epoch);
uint256 userPower = veZKCMemory.getPastStakingRewards(user, snapshotTime);
if (userPower == 0) continue;
uint256 totalPower = veZKCMemory.getPastTotalStakingRewards(snapshotTime);
if (totalPower == 0) continue;
uint256 emission = zkcMemory.getStakingEmissionsForEpoch(epoch);
rewards[i] = (emission * userPower) / totalPower;
}
return rewards;
}
/// @notice Internal function to claim rewards for a user in the given epochs
/// @param user The user address
/// @param epochs The epochs to claim rewards for
/// @param recipient The address to receive the minted rewards
/// @return amount The amount of rewards claimed
function _claim(address user, uint256[] calldata epochs, address recipient) internal returns (uint256) {
uint256[] memory amounts = _calculate(user, epochs);
ZKC zkcMemory = zkc;
uint256 amount;
uint256 currentEpoch = zkcMemory.getCurrentEpoch();
for (uint256 i; i < epochs.length; ++i) {
uint256 epoch = epochs[i];
if (_userClaimed[epoch][user]) revert AlreadyClaimed(epoch);
// Epoch must have ended
if (epoch >= currentEpoch) revert IZKC.EpochNotEnded(epoch);
_userClaimed[epoch][user] = true;
amount += amounts[i];
}
if (amount == 0) return 0;
zkcMemory.mintStakingRewardsForRecipient(recipient, amount);
return amount;
}
/// @notice Authorize upgrades to this contract
/// @param newImplementation The address of the new implementation
function _authorizeUpgrade(address newImplementation) internal override onlyRole(ADMIN_ROLE) {}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @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 reinitialization) 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 Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.22;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC-1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.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 EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* 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;
}
}
"
},
"src/ZKC.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {ERC20BurnableUpgradeable} from
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import {ERC20PermitUpgradeable} from
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Supply} from "./libraries/Supply.sol";
import {IZKC} from "./interfaces/IZKC.sol";
/// @title ZKC - ZK Coin
/// @notice The main ZKC token contract with epoch-based emissions
contract ZKC is
Initializable,
ERC20Upgradeable,
ERC20BurnableUpgradeable,
ERC20PermitUpgradeable,
AccessControlUpgradeable,
UUPSUpgradeable,
IZKC
{
/// @notice Address of the first initial minter
address public initialMinter1;
/// @notice Address of the second initial minter
address public initialMinter2;
/// @notice Remaining mintable amount for the first initial minter
uint256 public initialMinter1Remaining;
/// @notice Remaining mintable amount for the second initial minter
uint256 public initialMinter2Remaining;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @notice Admin role identifier
bytes32 public immutable ADMIN_ROLE = DEFAULT_ADMIN_ROLE;
/// @notice Initial token supply (1 billion ZKC)
uint256 public constant INITIAL_SUPPLY = Supply.INITIAL_SUPPLY;
/// @notice Duration of each epoch in seconds
uint256 public constant EPOCH_DURATION = Supply.EPOCH_DURATION;
/// @notice Number of epochs per year
uint256 public constant EPOCHS_PER_YEAR = Supply.EPOCHS_PER_YEAR;
/// @notice Basis points constant for percentage calculations
uint256 public constant BASIS_POINTS = 10000;
/// @notice Percentage of emissions allocated to PoVW rewards (75%)
uint256 public constant POVW_ALLOCATION_BPS = 7500;
/// @notice Percentage of emissions allocated to staking rewards (25%)
uint256 public constant STAKING_ALLOCATION_BPS = 2500;
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @notice Role identifier for PoVW reward minter
bytes32 public constant POVW_MINTER_ROLE = keccak256("POVW_MINTER_ROLE");
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
/// @notice Role identifier for staking reward minter
bytes32 public constant STAKING_MINTER_ROLE = keccak256("STAKING_MINTER_ROLE");
/// @notice Timestamp when epoch 0 started
/// @dev During initializeV2, this value is set to max uint256 to indicate that epoch 0 has not started
/// and prevent reward emissions functions from being callable before epoch 0 starts.
/// When initializeV3 is called, this value will be updated with the correct
/// start time of epoch 0, and the reward functions will be callable.
/// @dev Values of both 0 and type(uint256).max are both invalid values for epoch0StartTime.
uint256 public epoch0StartTime;
/// @notice Total amount of PoVW rewards claimed
uint256 public poVWClaimed;
/// @notice Total amount of staking rewards claimed
uint256 public stakingClaimed;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Internal function to check if epochs have started
/// @dev We defensively check for 0, even though it should be max uint256 if the epoch has not started.
/// @return bool True if epochs have started, false otherwise
function _epochsStarted() internal view returns (bool) {
return epoch0StartTime != 0 && epoch0StartTime != type(uint256).max;
}
/// @notice Initialize the ZKC token contract with initial minting parameters
/// @dev Sets up initial minters and their allocations. Can only be called once during deployment.
/// @param _initialMinter1 Address of the first initial minter
/// @param _initialMinter2 Address of the second initial minter
/// @param _initialMinter1Amount Amount that the first minter is allowed to mint
/// @param _initialMinter2Amount Amount that the second minter is allowed to mint
/// @param _owner Address that will be granted the admin role
function initialize(
address _initialMinter1,
address _initialMinter2,
uint256 _initialMinter1Amount,
uint256 _initialMinter2Amount,
address _owner
) public initializer {
__ERC20_init("ZK Coin", "ZKC");
__ERC20Permit_init("ZK Coin");
__AccessControl_init();
__UUPSUpgradeable_init();
require(
_initialMinter1Amount + _initialMinter2Amount == INITIAL_SUPPLY,
"Initial minter amounts must equal initial supply"
);
require(_initialMinter1 != address(0) || _initialMinter2 != address(0), "An initialMinter must be defined");
require(_owner != address(0), "Owner cannot be zero address");
initialMinter1 = _initialMinter1;
initialMinter2 = _initialMinter2;
initialMinter1Remaining = _initialMinter1Amount;
initialMinter2Remaining = _initialMinter2Amount;
_grantRole(ADMIN_ROLE, _owner);
}
/// @dev Must be called atomically during upgrade.
/// Set epoch0StartTime to max to indicate that epoch 0 has not started yet.
/// @notice Callable by anyone to initialize the contract to version 2
function initializeV2() public reinitializer(2) {
__ERC20Burnable_init();
epoch0StartTime = type(uint256).max;
}
/// @dev Set the epoch 0 start time to initiate the start of the first epoch.
/// @notice Only callable by the contract owner (admin role)
function initializeV3() public reinitializer(3) onlyRole(ADMIN_ROLE) {
epoch0StartTime = block.timestamp;
}
/// @inheritdoc IZKC
function initialMint(address[] calldata recipients, uint256[] calldata amounts) public {
require(recipients.length == amounts.length, "Recipients and amounts arrays must have equal length");
require(
msg.sender == initialMinter1 || msg.sender == initialMinter2, "Caller must be authorized initial minter"
);
uint256 minted;
for (uint256 i; i < recipients.length; ++i) {
uint256 amount = amounts[i];
_mint(recipients[i], amount);
minted += amount;
}
if (msg.sender == initialMinter1) {
initialMinter1Remaining -= minted;
} else {
initialMinter2Remaining -= minted;
}
}
/// @inheritdoc IZKC
function mintPoVWRewardsForRecipient(address recipient, uint256 amount) external onlyRole(POVW_MINTER_ROLE) {
_mintPoVWRewardsForRecipient(recipient, amount);
emit PoVWRewardsClaimed(recipient, amount);
}
/// @inheritdoc IZKC
function mintStakingRewardsForRecipient(address recipient, uint256 amount) external onlyRole(STAKING_MINTER_ROLE) {
_mintStakingRewardsForRecipient(recipient, amount);
emit StakingRewardsClaimed(recipient, amount);
}
function _mintPoVWRewardsForRecipient(address recipient, uint256 amount) internal {
uint256 totalEmissions = getTotalPoVWEmissionsAtEpochStart(getCurrentEpoch());
uint256 claimedTotal = poVWClaimed + amount;
if (claimedTotal > totalEmissions) {
revert TotalAllocationExceeded();
}
poVWClaimed = claimedTotal;
_mint(recipient, amount);
}
function _mintStakingRewardsForRecipient(address recipient, uint256 amount) internal {
uint256 totalEmissions = getTotalStakingEmissionsAtEpochStart(getCurrentEpoch());
uint256 claimedTotal = stakingClaimed + amount;
if (claimedTotal > totalEmissions) {
revert TotalAllocationExceeded();
}
stakingClaimed = claimedTotal;
_mint(recipient, amount);
}
/// @inheritdoc IZKC
function getSupplyAtEpochStart(uint256 epoch) public pure returns (uint256) {
return Supply.getSupplyAtEpoch(epoch);
}
/// @inheritdoc IZKC
function getTotalPoVWEmissionsAtEpochStart(uint256 epoch) public pure returns (uint256) {
uint256 totalEmissions = getSupplyAtEpochStart(epoch) - INITIAL_SUPPLY;
return (totalEmissions * POVW_ALLOCATION_BPS + BASIS_POINTS - 1) / BASIS_POINTS;
}
/// @inheritdoc IZKC
function getTotalStakingEmissionsAtEpochStart(uint256 epoch) public pure returns (uint256) {
uint256 totalEmissions = getSupplyAtEpochStart(epoch) - INITIAL_SUPPLY;
return (totalEmissions * STAKING_ALLOCATION_BPS) / BASIS_POINTS;
}
/// @inheritdoc IZKC
function getEmissionsForEpoch(uint256 epoch) public returns (uint256) {
return Supply.getEmissionsForEpoch(epoch);
}
/// @inheritdoc IZKC
function getPoVWEmissionsForEpoch(uint256 epoch) public returns (uint256) {
uint256 totalEmission = getEmissionsForEpoch(epoch);
// Round up povw emissions. Combined with staking emissions rounding down,
// this ensures we don't leave any dust.
return (totalEmission * POVW_ALLOCATION_BPS + BASIS_POINTS - 1) / BASIS_POINTS;
}
/// @inheritdoc IZKC
function getStakingEmissionsForEpoch(uint256 epoch) public returns (uint256) {
uint256 totalEmission = getEmissionsForEpoch(epoch);
// Round down staking emissions. Combined with povw emissions rounding up,
// this ensures we don't leave any dust.
return (totalEmission * STAKING_ALLOCATION_BPS) / BASIS_POINTS;
}
/// @inheritdoc IZKC
function getCurrentEpoch() public view returns (uint256) {
if (!_epochsStarted()) revert EpochsNotStarted();
return (block.timestamp - epoch0StartTime) / EPOCH_DURATION;
}
/// @inheritdoc IZKC
function getCurrentEpochEndTime() public view returns (uint256) {
return getEpochEndTime(getCurrentEpoch());
}
/// @inheritdoc IZKC
function getEpochStartTime(uint256 epoch) public view returns (uint256) {
if (!_epochsStarted()) revert EpochsNotStarted();
return epoch0StartTime + (epoch * EPOCH_DURATION);
}
/// @inheritdoc IZKC
function getEpochEndTime(uint256 epoch) public view returns (uint256) {
if (!_epochsStarted()) revert EpochsNotStarted();
return getEpochStartTime(epoch + 1) - 1;
}
/// @notice Get the total supply at the current epoch.
/// @dev Does not include rewards that will be emitted at the end of the current epoch.
/// @dev Overrides ERC20 totalSupply to return epoch-based theoretical supply, however
/// not all tokens may have been claimed (and thus minted) by recipients yet.
/// @return The theoretical total supply of ZKC tokens at the current epoch
function totalSupply() public view override returns (uint256) {
if (!_epochsStarted()) {
return INITIAL_SUPPLY;
}
return getSupplyAtEpochStart(getCurrentEpoch());
}
/// @inheritdoc IZKC
function claimedTotalSupply() public view returns (uint256) {
return super.totalSupply();
}
/// @notice Authorize contract upgrades (UUPS pattern)
/// @dev Only accounts with ADMIN_ROLE can authorize upgrades
/// @param newImplementation Address of the new implementation contract
function _authorizeUpgrade(address newImplementation) internal override onlyRole(ADMIN_ROLE) {}
}
"
},
"src/interfaces/IZKC.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/// @title IZKC
/// @notice Interface for the ZKC token with epoch-based emissions
/// @dev Defines ZKC-specific functionality for epoch-based reward distribution
interface IZKC {
/// @notice Emitted when a recipient claims PoVW rewards.
/// @param recipient The address that claimed the rewards
/// @param amount The amount of ZKC tokens claimed
/// @dev The reward amount could include ZKC that was earned across multiple epochs.
event PoVWRewardsClaimed(address indexed recipient, uint256 amount);
/// @notice Emitted when a recipient claims staking rewards.
/// @param recipient The address that claimed the rewards
/// @param amount The amount of ZKC tokens claimed
/// @dev The reward amount could include ZKC that was earned across multiple epochs.
event StakingRewardsClaimed(address indexed recipient, uint256 amount);
error EpochNotEnded(uint256 epoch);
error TotalAllocationExceeded();
error EpochsNotStarted();
/// @notice Perform initial token distribution to specified recipients
/// @dev Only callable by designated initial minters
/// @param recipients Array of addresses to receive tokens
/// @param amounts Array of token amounts corresponding to each recipient
function initialMint(address[] calldata recipients, uint256[] calldata amounts) external;
/// @notice Mint PoVW rewards for a specific recipient
/// @dev Only callable by addresses with POVW_MINTER_ROLE
/// @param recipient Address to receive the minted rewards
/// @param amount Amount of tokens to mint
function mintPoVWRewardsForRecipient(address recipient, uint256 amount) external;
/// @notice Mint staking rewards for a specific recipient
/// @dev Only callable by addresses with STAKING_MINTER_ROLE
/// @param recipient Address to receive the minted rewards
/// @param amount Amount of tokens to mint
function mintStakingRewardsForRecipient(address recipient, uint256 amount) external;
/// @notice Get the total supply at the start of a specific epoch
/// @dev ZKC is emitted at the end of each epoch, so this excludes rewards generated
/// as part of staking/work during the current epoch.
/// @param epoch The epoch number (0-indexed)
/// @return The total supply at the start of the epoch
function getSupplyAtEpochStart(uint256 epoch) external pure returns (uint256);
/// @notice Get the cumulative total PoVW emissions since genesis up to the start of a specific epoch
/// @param epoch The epoch number
/// @return Total PoVW emissions up to the epoch start
function getTotalPoVWEmissionsAtEpochStart(uint256 epoch) external returns (uint256);
/// @notice Get the cumulative total staking emissions since genesis up to the start of a specific epoch
/// @param epoch The epoch number
/// @return Total staking emissions up to the epoch start
function getTotalStakingEmissionsAtEpochStart(uint256 epoch) external returns (uint256);
/// @notice Get the total ZKC that will be emitted at the _end_ of the specified epoch
/// @dev Includes both PoVW and staking rewards
/// @param epoch The epoch number
/// @return Total emissions for the epoch
function getEmissionsForEpoch(uint256 epoch) external returns (uint256);
/// @notice Get the PoVW emissions that will be emitted at the _end_ of the specified epoch
/// @param epoch The epoch number
/// @return PoVW emissions for the epoch
function getPoVWEmissionsForEpoch(uint256 epoch) external returns (uint256);
/// @notice Get the staking emissions that will be emitted at the _end_ of the specified epoch
/// @param epoch The epoch number
/// @return Staking emissions for the epoch
function getStakingEmissionsForEpoch(uint256 epoch) external returns (uint256);
/// @notice Get the current epoch number
/// @dev Calculated based on time elapsed since deployment.
/// @dev Reverts if epochs have not started yet.
/// @return The current epoch number (0-indexed)
function getCurrentEpoch() external view returns (uint256);
/// @notice Get the current epoch end time
/// @dev Returns the final timestamp at which the current epoch is active.
/// After this time, rewards will be emitted.
/// @return The timestamp when the current epoch ends
function getCurrentEpochEndTime() external view returns (uint256);
/// @notice Get the start timestamp of a specific epoch
/// @dev Reverts if epochs have not started yet.
/// @param epoch The epoch number
/// @return The timestamp when the epoch starts
function getEpochStartTime(uint256 epoch) external view returns (uint256);
/// @notice Get the end timestamp of a specific epoch
/// @dev Returns the final timestamp at which the epoch is active
/// @dev Reverts if epochs have not started yet.
/// @param epoch The epoch number
/// @return The timestamp when the epoch ends
function getEpochEndTime(uint256 epoch) external view returns (uint256);
/// @notice Get the actual minted and claimed total supply
/// @dev This represents the initial supply that was minted and allocated to initial minters,
/// as well as tokens that have been claimed (and thus minted) via PoVW or Staking rewards.
/// @return The total amount of tokens that have been claimed
function claimedTotalSupply() external view returns (uint256);
}
"
},
"src/interfaces/IRewards.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
/// @title IRewards
/// @notice Interface for reward distribution calculations
/// @dev Used by external contracts to determine reward allocations based on stake amounts
interface IRewards {
// Custom errors
error CannotDelegateRewardsWhileWithdrawing();
error RewardsExpiredSignature(uint256 expiry);
/// @notice Emitted when an account changes their reward delegation
/// @param delegator The account that changed their delegation
/// @param fromDelegate The previous delegate (or the delegator if they were self-delegated)
/// @param toDelegate The new delegate (or the delegator if they are self-delegating)
event RewardDelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event DelegateRewardsChanged(address indexed delegate, uint256 previousRewards, uint256 newRewards);
/// @notice Get current staking rewards power for an account
/// @param account Account to query
/// @return Reward power (staked amount / REWARD_POWER_SCALAR)
function getStakingRewards(address account) external view returns (uint256);
/// @notice Get historical staking rewards power for an account
/// @param account Account to query
/// @param timepoint Historical timestamp to query
/// @return Reward power at the specified timestamp
function getPastStakingRewards(address account, uint256 timepoint) external view returns (uint256);
/// @notice Get total staking rewards power across all users
/// @return Total reward power
function getTotalStakingRewards() external view returns (uint256);
/// @notice Get historical total staking rewards power
/// @param timepoint Historical timestamp to query
/// @return Total reward power at the specified timestamp
function getPastTotalStakingRewards(uint256 timepoint) external view returns (uint256);
/// @notice Get current PoVW reward cap for an account
/// @param account Account to query
/// @return PoVW reward cap (staked amount / POVW_REWARD_CAP_SCALAR)
function getPoVWRewardCap(address account) external view returns (uint256);
/// @notice Get historical PoVW reward cap for an account
/// @param account Account to query
/// @param timepoint Historical timestamp to query
/// @return PoVW reward cap at the specified timestamp
function getPastPoVWRewardCap(address account, uint256 timepoint) external view returns (uint256);
/// @notice Returns the reward delegate chosen by an account
/// @param account Account to query
/// @return The address that account has delegated rewards to (or account itself if none)
function rewardDelegates(address account) external view returns (address);
/// @notice Delegate reward power to another address
/// @param delegatee Address to delegate rewards to
function delegateRewards(address delegatee) external;
/// @notice Delegate rewards using a signature
/// @param delegatee Address to delegate rewards to
/// @param nonce Nonce for the signature
/// @param expiry Expiration timestamp for the signature
/// @param v Recovery byte of the signature
/// @param r R component of the signature
/// @param s S component of the signature
function delegateRewardsBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s)
external;
}
"
},
"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted to signal this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.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
Submitted on: 2025-10-01 12:42:58
Comments
Log in to comment.
No comments yet.