Source Code
Latest 25 from a total of 10,796 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Batch Claim Rewa... | 25443167 | 2 hrs ago | IN | 0 HYPE | 0.00005061 | ||||
| Batch Claim Rewa... | 25422526 | 8 hrs ago | IN | 0 HYPE | 0.00004574 | ||||
| Batch Claim Rewa... | 25412353 | 11 hrs ago | IN | 0 HYPE | 0.00028174 | ||||
| Batch Claim Rewa... | 25407845 | 12 hrs ago | IN | 0 HYPE | 0.00013591 | ||||
| Batch Claim Rewa... | 25397957 | 15 hrs ago | IN | 0 HYPE | 0.0002571 | ||||
| Claim Reward | 25396291 | 15 hrs ago | IN | 0 HYPE | 0.00000586 | ||||
| Claim Reward | 25396213 | 15 hrs ago | IN | 0 HYPE | 0.00000821 | ||||
| Batch Claim Rewa... | 25396128 | 15 hrs ago | IN | 0 HYPE | 0.00079717 | ||||
| Claim Reward | 25375710 | 21 hrs ago | IN | 0 HYPE | 0.00000396 | ||||
| Claim Reward | 25374510 | 21 hrs ago | IN | 0 HYPE | 0.00000396 | ||||
| Claim Reward | 25374500 | 21 hrs ago | IN | 0 HYPE | 0.00000827 | ||||
| Batch Claim Rewa... | 25373430 | 21 hrs ago | IN | 0 HYPE | 0.00025954 | ||||
| Batch Claim Rewa... | 25336544 | 31 hrs ago | IN | 0 HYPE | 0.00069678 | ||||
| Batch Claim Rewa... | 25326316 | 34 hrs ago | IN | 0 HYPE | 0.00020493 | ||||
| Batch Claim Rewa... | 25269535 | 2 days ago | IN | 0 HYPE | 0.00001415 | ||||
| Batch Claim Rewa... | 25269280 | 2 days ago | IN | 0 HYPE | 0.00011482 | ||||
| Batch Claim Rewa... | 25218007 | 2 days ago | IN | 0 HYPE | 0.00005034 | ||||
| Batch Claim Rewa... | 25103483 | 3 days ago | IN | 0 HYPE | 0.00101717 | ||||
| Batch Claim Rewa... | 25086737 | 4 days ago | IN | 0 HYPE | 0.00002343 | ||||
| Claim Reward | 25081525 | 4 days ago | IN | 0 HYPE | 0.00002011 | ||||
| Claim Reward | 25074207 | 4 days ago | IN | 0 HYPE | 0.00284615 | ||||
| Batch Claim Rewa... | 25073677 | 4 days ago | IN | 0 HYPE | 0.0001323 | ||||
| Claim Reward | 25073589 | 4 days ago | IN | 0 HYPE | 0.00021446 | ||||
| Claim Reward | 25073547 | 4 days ago | IN | 0 HYPE | 0.00010675 | ||||
| Claim Reward | 25073530 | 4 days ago | IN | 0 HYPE | 0.00009507 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Distributor
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 10000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { MerkleProofLib } from "@solady/contracts/utils/MerkleProofLib.sol";
/**
* @title Distributor
* @notice A contract for distributing rewards from a merkle root.
* @author @gfranchi3s - FelixLabs
* add new users ---> possible
* remove users ----> possible if not already claimed
* increase allocation for a user ---> possible in the same distribution if not claimed || possible creating a new
* allocation (does not require delays in time)
* decrease allocation for a user ---> possible if not claimed
* @notice The contract is pausable in order to avoid race conditions between the off-chain process and the on-chain
* process.
* @notice The multicall is used primarily to batch together the setting of the merkle root and the unpausing of the
* contract.
* @notice In order to use the multicall the msg.sender must have the PAUSER_ROLE and the MERKLE_ROOT_SETTER_ROLE.
*/
contract Distributor is AccessControl, Multicall, Pausable {
using SafeERC20 for IERC20;
using MerkleProofLib for bytes32[];
/**
* @notice A struct that contains the information about a distribution.
* @param merkleRoot The merkle root of the distribution.
* @param rewardAmount The amount of rewards to distribute.
* @param claimedAmount The amount of rewards that have been claimed.
*/
struct DistributionInfo {
bytes32 merkleRoot;
uint256 rewardAmount;
uint256 claimedAmount;
}
/**
* @notice A struct that contains the information about a claim.
* @param distributionId The id of the distribution.
* @param amount The amount of rewards to claim.
*/
struct ClaimInfo {
uint256 distributionId;
uint256 amount;
}
/// @notice It is thrown when the merkle root is bytes32(0).
error Distributor__InvalidMerkleRoot();
/// @notice It is thrown when the address is 0.
error Distributor__ZeroAddress();
/// @notice It is thrown when the amount is 0.
error Distributor__ZeroAmount();
/// @notice It is thrown when the distribution does not exist.
error Distributor__DistributionDoesNotExist();
/// @notice It is thrown when the new reward amount is not greater than the claimed amount.
error Distributor__NewRewardAmountNotGreaterThanOrEqualClaimedAmount();
/// @notice It is thrown when the distributor has no rewards left in a distribution.
error Distributor__NoRewardsLeft();
/// @notice It is thrown when the proof is invalid.
error Distributor__InvalidProof();
/// @notice It is thrown when the batch claim size is invalid.
error Distributor__InvalidBatchClaimSize();
/// @notice It is thrown when the batch set merkle root size is invalid.
error Distributor__InvalidBatchSetMerkleRootSize();
/// @notice It is thrown when the merkle root is the same.
error Distributor__SameMerkleRoot();
/// @notice It is thrown when the unclaimed rewards receiver is the same.
error Distributor__SameUnclaimedRewardsReceiver();
/// @notice It is thrown when the user has already claimed.
error Distributor__UserHasAlreadyClaimed();
/// @notice It is thrown when the unclaimed rewards receiver is not pending.
error Distributor__NotPendingUnclaimedRewardsReceiver();
/**
* @notice It is emitted when the contract is deployed.
* @param _rewardToken The address of the reward token.
* @param _unclaimedRewardsReceiver The address of the unclaimed rewards receiver.
* @param _timestamp The timestamp of the deployment.
*/
event Genesis(address indexed _rewardToken, address indexed _unclaimedRewardsReceiver, uint256 _timestamp);
/**
* @notice It is emitted when a new unclaimed rewards receiver is proposed.
* @param _newUnclaimedRewardsReceiver The address of the new unclaimed rewards receiver.
* @param _timestamp The timestamp of the proposal.
*/
event UnclaimedRewardsReceiverProposed(address indexed _newUnclaimedRewardsReceiver, uint256 _timestamp);
/**
* @notice It is emitted when a new unclaimed rewards receiver is set.
* @param _oldUnclaimedRewardsReceiver The address of the old unclaimed rewards receiver.
* @param _newUnclaimedRewardsReceiver The address of the new unclaimed rewards receiver.
* @param _timestamp The timestamp of the setting.
*/
event UnclaimedRewardsReceiverSet(
address indexed _oldUnclaimedRewardsReceiver, address indexed _newUnclaimedRewardsReceiver, uint256 _timestamp
);
/**
* @notice It is emitted when a new distribution is created.
* @param _distributionId The id of the distribution.
* @param _merkleRoot The merkle root of the distribution.
* @param _rewardAmount The amount of rewards to distribute.
*/
event DistributionCreated(
uint256 indexed _distributionId, bytes32 indexed _merkleRoot, uint256 indexed _rewardAmount
);
/**
* @notice It is emitted when a reward token is withdrawn while setting a new merkle root.
* @param _amount The amount of rewards to withdraw.
* @param _timestamp The timestamp of the withdrawal.
*/
event RewardTokenWithdrawn(uint256 indexed _amount, uint256 indexed _timestamp);
/**
* @notice It is emitted when a reward token is added while setting a new merkle root.
* @param _amount The amount of rewards added.
* @param _timestamp The timestamp of the addition.
*/
event RewardTokenPushed(uint256 indexed _amount, uint256 indexed _timestamp);
/**
* @notice It is emitted when a distribution is updated.
* @param _distributionId The id of the distribution.
* @param _merkleRoot The merkle root of the distribution.
* @param _rewardAmount The amount of rewards to distribute.
*/
event DistributionUpdated(
uint256 indexed _distributionId, bytes32 indexed _merkleRoot, uint256 indexed _rewardAmount
);
/**
* @notice It is emitted when a batch of distributions is updated.
* @param _distributionIds The ids of the distributions.
* @param _merkleRoots The merkle roots of the distributions.
* @param _rewardAmounts The amounts of rewards to distribute.
*/
event BatchDistributionUpdated(
uint256[] indexed _distributionIds, bytes32[] indexed _merkleRoots, uint256[] indexed _rewardAmounts
);
/**
* @notice It is emitted when a reward token is claimed.
* @param _distributionId The id of the distribution.
* @param _user The address of the user.
* @param _amount The amount of rewards claimed.
*/
event RewardTokenClaimed(uint256 indexed _distributionId, address indexed _user, uint256 indexed _amount);
/**
* @notice It is emitted when a batch of rewards is claimed.
* @param _claimInfos The claim infos.
* @param _user The address of the user.
* @param _amount The amount of rewards claimed.
*/
event RewardTokenClaimedBatch(ClaimInfo[] indexed _claimInfos, address indexed _user, uint256 indexed _amount);
/*//////////////////////////////////////////////////////////////
/////////////////////// VARIABLES AND CONSTANTS /////////////////
//////////////////////////////////////////////////////////////*/
/// @notice The role who is allowed to add new distributions.
bytes32 public constant DISTRIBUTOR_ADMIN_ROLE = keccak256("DISTRIBUTOR_ADMIN_ROLE");
/// @notice The role who is allowed to set the unclaimed rewards receiver.
bytes32 public constant UNCLAIMED_RECEIVER_SETTER_ROLE = keccak256("UNCLAIMED_RECEIVER_SETTER_ROLE");
/// @notice The role who is allowed to set the merkle root.
bytes32 public constant MERKLE_ROOT_SETTER_ROLE = keccak256("MERKLE_ROOT_SETTER_ROLE");
/// @notice The role who is allowed to pause the contract.
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/// @notice The reward token.
IERC20 public immutable REWARD_TOKEN;
/// @notice The unclaimed rewards receiver.
address public unclaimedRewardsReceiver;
/// @notice The pending unclaimed rewards receiver.
address public pendingUnclaimedRewardsReceiver;
/// @notice The counter for the distribution ids.
uint256 public distributionIdCounter;
/// @notice The mapping of the distribution ids to the distribution infos.
mapping(uint256 distributionId => DistributionInfo distributionInfo) public distributionInfo;
/// @notice The mapping of the distribution ids to the user claims.
mapping(uint256 distributionId => mapping(address user => bool isClaimed)) public userClaims;
/*//////////////////////////////////////////////////////////////
/////////////////////// CONSTRUCTOR ////////////////////////////
//////////////////////////////////////////////////////////////*/
/**
* @notice It is the constructor of the contract.
* @param _rewardToken The address of the reward token.
* @param _admin The address of the admin.
* @param _unclaimedRewardsReceiver The address of the unclaimed rewards receiver.
* @param _unclaimedRewardsReceiverSetter The address of the unclaimed rewards receiver setter.
* @param _distributorAdmin The address of the distributor admin.
* @param _merkleRootSetter The address of the merkle root setter.
*/
constructor(
address _rewardToken,
address _admin,
address _unclaimedRewardsReceiver,
address _unclaimedRewardsReceiverSetter,
address _distributorAdmin,
address _merkleRootSetter,
address _pauserRole
) {
_requireNotZeroAddress(_rewardToken);
_requireNotZeroAddress(_admin);
_requireNotZeroAddress(_unclaimedRewardsReceiver);
_requireNotZeroAddress(_unclaimedRewardsReceiverSetter);
_requireNotZeroAddress(_distributorAdmin);
_requireNotZeroAddress(_merkleRootSetter);
_requireNotZeroAddress(_pauserRole);
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(DISTRIBUTOR_ADMIN_ROLE, _distributorAdmin);
_grantRole(UNCLAIMED_RECEIVER_SETTER_ROLE, _unclaimedRewardsReceiverSetter);
_grantRole(MERKLE_ROOT_SETTER_ROLE, _merkleRootSetter);
_grantRole(PAUSER_ROLE, _pauserRole);
REWARD_TOKEN = IERC20(_rewardToken);
unclaimedRewardsReceiver = _unclaimedRewardsReceiver;
emit Genesis(_rewardToken, _unclaimedRewardsReceiver, block.timestamp);
}
/*//////////////////////////////////////////////////////////////
/////////////////////// EXTERNAL FUNCTIONS ///////////////////////
//////////////////////////////////////////////////////////////*/
/**
* @notice It is the function to pause the contract.
* @dev Only the pauser role can call this function.
*/
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
/**
* @notice It is the function to unpause the contract.
* @dev Only the pauser role can call this function.
*/
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
/**
* @notice It is the function to propose a new unclaimed rewards receiver.
* @param _newUnclaimedRewardsReceiver The address of the new unclaimed rewards receiver.
* @dev Only the unclaimed rewards receiver setter role can call this function.
* @dev It uses a two-step process to set the new unclaimed rewards receiver.
* @dev The new receiver cannot be the same as the current receiver.
* @dev The new receiver cannot be the zero address.
*/
function proposeUnclaimedRewardsReceiver(address _newUnclaimedRewardsReceiver)
external
onlyRole(UNCLAIMED_RECEIVER_SETTER_ROLE)
{
_requireNotZeroAddress(_newUnclaimedRewardsReceiver);
_requireNotSameUnclaimedRewardsReceiver(_newUnclaimedRewardsReceiver, unclaimedRewardsReceiver);
pendingUnclaimedRewardsReceiver = _newUnclaimedRewardsReceiver;
emit UnclaimedRewardsReceiverProposed(_newUnclaimedRewardsReceiver, block.timestamp);
}
/**
* @notice It is the function to accept the new unclaimed rewards receiver.
* @dev Only the pending unclaimed rewards receiver can call this function.
* @dev It sets the new unclaimed rewards receiver.
* @dev It sets the pending unclaimed rewards receiver to the zero address.
*/
function acceptUnclaimedRewardsReceiver() external {
_requireMsgSenderIsPendingUnclaimedRewardsReceiver();
address _oldUnclaimedRewardsReceiver = unclaimedRewardsReceiver;
address _newUnclaimedRewardsReceiver = pendingUnclaimedRewardsReceiver;
unclaimedRewardsReceiver = _newUnclaimedRewardsReceiver;
pendingUnclaimedRewardsReceiver = address(0);
emit UnclaimedRewardsReceiverSet(_oldUnclaimedRewardsReceiver, _newUnclaimedRewardsReceiver, block.timestamp);
}
/**
* @notice It is the function to create a new distribution.
* @param _merkleRoot The merkle root of the distribution.
* @param _rewardAmount The amount of rewards to distribute.
* @dev Only the distributor admin role can call this function.
* @dev The merkle root cannot be zero bytes32.
* @dev The reward amount cannot be zero.
* @dev It is up to the off-chain process to ensure the merkle root is valid and matches the reward amount.
*/
function createDistribution(bytes32 _merkleRoot, uint256 _rewardAmount) external onlyRole(DISTRIBUTOR_ADMIN_ROLE) {
_requireValidMerkleRoot(_merkleRoot);
_requireNotZeroAmount(_rewardAmount);
DistributionInfo memory _distributionInfo =
DistributionInfo({ merkleRoot: _merkleRoot, rewardAmount: _rewardAmount, claimedAmount: 0 });
uint256 distributionId = ++distributionIdCounter;
distributionInfo[distributionId] = _distributionInfo;
_pullRewardToken(_msgSender(), _distributionInfo.rewardAmount);
emit DistributionCreated(distributionId, _merkleRoot, _rewardAmount);
}
/**
* @notice It is the function to override the merkle root of a distribution.
* @param _distributionId The id of the distribution.
* @param _merkleRoot The merkle root of the distribution.
* @param _newRewardAmount The amount of rewards to distribute.
* @dev Only the merkle root setter role can call this function.
* @dev The merkle root cannot be zero bytes32.
* @dev The reward amount cannot be zero.
* @notice This function is used also to add or withdraw rewards from the contract.
*/
function setMerkleRoot(
uint256 _distributionId,
bytes32 _merkleRoot,
uint256 _newRewardAmount
)
external
onlyRole(MERKLE_ROOT_SETTER_ROLE)
{
(uint256 _amountToAdd, uint256 _amountToSubtract) =
_setMerkleRoot(_distributionId, _merkleRoot, _newRewardAmount);
if (_amountToAdd > _amountToSubtract) {
_pullRewardToken(_msgSender(), _amountToAdd);
emit RewardTokenPushed(_amountToAdd, block.timestamp);
} else if (_amountToAdd < _amountToSubtract) {
_pushRewardToken(unclaimedRewardsReceiver, _amountToSubtract);
emit RewardTokenWithdrawn(_amountToSubtract, block.timestamp);
}
emit DistributionUpdated(_distributionId, _merkleRoot, _newRewardAmount);
}
/**
* @notice It is the function to batch set the merkle root of multiple distributions.
* @param _distributionIds The ids of the distributions.
* @param _merkleRoots The merkle roots of the distributions.
* @param _newRewardAmounts The amounts of rewards to distribute.
* @dev Only the merkle root setter role can call this function.
* @dev The size of the arrays must be the same.
* @dev It is assumed that if the size of the arrays is too big the transaction could revert due to OOG. This is not
* a problem as it can be broken down into smaller chunks.
*/
function batchSetMerkleRoot(
uint256[] calldata _distributionIds,
bytes32[] calldata _merkleRoots,
uint256[] calldata _newRewardAmounts
)
external
onlyRole(MERKLE_ROOT_SETTER_ROLE)
{
_requireBatchSetMerkleRootSizeIsValid(_distributionIds.length, _merkleRoots.length, _newRewardAmounts.length);
uint256 _totalAmountToAdd;
uint256 _totalAmountToSubtract;
for (uint256 i = 0; i < _distributionIds.length; ++i) {
(uint256 _amountToAdd_, uint256 _amountToSubtract_) =
_setMerkleRoot(_distributionIds[i], _merkleRoots[i], _newRewardAmounts[i]);
_totalAmountToAdd += _amountToAdd_;
_totalAmountToSubtract += _amountToSubtract_;
}
if (_totalAmountToAdd > _totalAmountToSubtract) {
_pullRewardToken(_msgSender(), _totalAmountToAdd - _totalAmountToSubtract);
emit RewardTokenPushed(_totalAmountToAdd - _totalAmountToSubtract, block.timestamp);
} else if (_totalAmountToAdd < _totalAmountToSubtract) {
_pushRewardToken(unclaimedRewardsReceiver, _totalAmountToSubtract - _totalAmountToAdd);
emit RewardTokenWithdrawn(_totalAmountToSubtract - _totalAmountToAdd, block.timestamp);
}
emit BatchDistributionUpdated(_distributionIds, _merkleRoots, _newRewardAmounts);
}
/**
* @notice It is the function to claim a reward.
* @param _claimInfos The claim infos.
* @param _proofs The proofs.
*/
function claimReward(ClaimInfo calldata _claimInfos, bytes32[] calldata _proofs) external whenNotPaused {
address _msgSender_ = _msgSender();
_claimReward(_claimInfos, _proofs, _msgSender_);
_pushRewardToken(_msgSender_, _claimInfos.amount);
emit RewardTokenClaimed(_claimInfos.distributionId, _msgSender_, _claimInfos.amount);
}
/**
* @notice It is the function to batch claim rewards.
* @param _claimInfos The claim infos.
* @param _proofs The proofs.
* @dev The size of the arrays must be the same.
* @dev It is assumed that if the size of the arrays is too big the transaction could revert due to OOG. This is not
* a problem as it can be broken down into smaller chunks.
*/
function batchClaimRewards(ClaimInfo[] calldata _claimInfos, bytes32[][] calldata _proofs) external whenNotPaused {
_requireBatchClaimSizeIsValid(_claimInfos.length, _proofs.length);
address _msgSender_ = _msgSender();
uint256 _claimedAmount;
for (uint256 i = 0; i < _claimInfos.length; ++i) {
_claimedAmount += _claimReward(_claimInfos[i], _proofs[i], _msgSender_);
}
_pushRewardToken(_msgSender_, _claimedAmount);
emit RewardTokenClaimedBatch(_claimInfos, _msgSender_, _claimedAmount);
}
function getDistributionInfo(uint256 _distributionId) external view returns (DistributionInfo memory) {
return distributionInfo[_distributionId];
}
/*//////////////////////////////////////////////////////////////
/////////////////////// PRIVATE FUNCTIONS ///////////////////////
//////////////////////////////////////////////////////////////*/
function _setMerkleRoot(
uint256 _distributionId,
bytes32 _merkleRoot,
uint256 _newRewardAmount
)
private
returns (uint256 _amountToAdd, uint256 _amountToSubtract)
{
_requireValidMerkleRoot(_merkleRoot);
_requireNotZeroAmount(_newRewardAmount);
DistributionInfo memory _cachedDistributionInfo = distributionInfo[_distributionId];
_requireDistributionExists(_cachedDistributionInfo);
_requireNotSameMerkleRoot(_merkleRoot, _cachedDistributionInfo.merkleRoot);
_requireNewRewardAmountGEClaimedAmount(_newRewardAmount, _cachedDistributionInfo.claimedAmount);
DistributionInfo memory _newDistributionInfo = DistributionInfo({
merkleRoot: _merkleRoot,
rewardAmount: _newRewardAmount,
claimedAmount: _cachedDistributionInfo.claimedAmount
});
distributionInfo[_distributionId] = _newDistributionInfo;
if (_newRewardAmount > _cachedDistributionInfo.rewardAmount) {
_amountToAdd = _newRewardAmount - _cachedDistributionInfo.rewardAmount;
} else if (_newRewardAmount < _cachedDistributionInfo.rewardAmount) {
_amountToSubtract = _cachedDistributionInfo.rewardAmount - _newRewardAmount;
}
}
function _claimReward(
ClaimInfo calldata _claimInfos,
bytes32[] calldata _proofs,
address _user
)
private
returns (uint256 _rewardAmount)
{
_requireNotZeroAmount(_claimInfos.amount);
DistributionInfo memory _distributionInfo = distributionInfo[_claimInfos.distributionId];
_requireDistributionExists(_distributionInfo);
_requireDistributionHasStillRewards(
_distributionInfo.rewardAmount - _distributionInfo.claimedAmount, _claimInfos.amount
);
_requireMsgUserHasNotClaimed(_claimInfos.distributionId, _user);
/// @dev It is doubled hashed because of how the OZ library generates the proof. It's done for avoiding second
/// preimage attacks even though such an attack is not possible in this case.
bytes32 _valueHash = keccak256(abi.encode(_user, _claimInfos.amount, _claimInfos.distributionId));
bytes32 _leaf = keccak256(abi.encode(_valueHash));
if (!_proofs.verifyCalldata(_distributionInfo.merkleRoot, _leaf)) revert Distributor__InvalidProof();
userClaims[_claimInfos.distributionId][_user] = true;
_distributionInfo.claimedAmount += _claimInfos.amount;
distributionInfo[_claimInfos.distributionId] = _distributionInfo;
return _claimInfos.amount;
}
function _pullRewardToken(address _from, uint256 _amount) private {
REWARD_TOKEN.safeTransferFrom(_from, address(this), _amount);
}
function _pushRewardToken(address _to, uint256 _amount) private {
REWARD_TOKEN.safeTransfer(_to, _amount);
}
function _requireNewRewardAmountGEClaimedAmount(uint256 _newRewardAmount, uint256 _claimedAmount) private pure {
if (_newRewardAmount < _claimedAmount) revert Distributor__NewRewardAmountNotGreaterThanOrEqualClaimedAmount();
}
function _requireMsgSenderIsPendingUnclaimedRewardsReceiver() private view {
if (_msgSender() != pendingUnclaimedRewardsReceiver) revert Distributor__NotPendingUnclaimedRewardsReceiver();
}
function _requireMsgUserHasNotClaimed(uint256 _distributionId, address _user) private view {
if (userClaims[_distributionId][_user]) revert Distributor__UserHasAlreadyClaimed();
}
function _requireDistributionExists(DistributionInfo memory _distributionInfo) private pure {
if (_distributionInfo.merkleRoot == bytes32(0)) revert Distributor__DistributionDoesNotExist();
}
function _requireNotZeroAddress(address _address) private pure {
if (_address == address(0)) revert Distributor__ZeroAddress();
}
function _requireNotZeroAmount(uint256 _amount) private pure {
if (_amount == 0) revert Distributor__ZeroAmount();
}
function _requireValidMerkleRoot(bytes32 _merkleRoot) private pure {
if (_merkleRoot == bytes32(0)) revert Distributor__InvalidMerkleRoot();
}
function _requireDistributionHasStillRewards(uint256 _remainingRewards, uint256 _claimedAmount) private pure {
if (_remainingRewards < _claimedAmount) revert Distributor__NoRewardsLeft();
}
function _requireBatchClaimSizeIsValid(uint256 _claimInfosLength, uint256 _proofsLength) private pure {
if (_claimInfosLength != _proofsLength) revert Distributor__InvalidBatchClaimSize();
}
function _requireNotSameMerkleRoot(bytes32 _newMerkleRoot, bytes32 _oldMerkleRoot) private pure {
if (_newMerkleRoot == _oldMerkleRoot) revert Distributor__SameMerkleRoot();
}
function _requireNotSameUnclaimedRewardsReceiver(
address _newUnclaimedRewardsReceiver,
address _oldUnclaimedRewardsReceiver
)
private
pure
{
if (_newUnclaimedRewardsReceiver == _oldUnclaimedRewardsReceiver) {
revert Distributor__SameUnclaimedRewardsReceiver();
}
}
function _requireBatchSetMerkleRootSizeIsValid(
uint256 _distributionIdsLength,
uint256 _merkleRootsLength,
uint256 _newRewardAmountsLength
)
private
pure
{
if (_distributionIdsLength != _merkleRootsLength || _distributionIdsLength != _newRewardAmountsLength) {
revert Distributor__InvalidBatchSetMerkleRootSize();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.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 AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @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);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
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) {
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) {
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 {
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) {
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) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Multicall.sol)
pragma solidity ^0.8.20;
import {Address} from "./Address.sol";
import {Context} from "./Context.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
* careful about sending transactions invoking {multicall}. For example, a relay address that filters function
* selectors won't filter calls nested within a {multicall} operation.
*
* NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {Context-_msgSender}).
* If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
* to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
* {Context-_msgSender} are not propagated to subcalls.
*/
abstract contract Multicall is Context {
/**
* @dev Receives and executes a batch of function calls on this contract.
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
bytes memory context = msg.sender == _msgSender()
? new bytes(0)
: msg.data[msg.data.length - _contextSuffixLength():];
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
}
return results;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// 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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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 Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @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
* {Errors.FailedCall} 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 Errors.InsufficientBalance(address(this).balance, value);
}
(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 {Errors.FailedCall}) 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 {Errors.FailedCall} 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 {Errors.FailedCall}.
*/
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
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}{
"remappings": [
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@solady/contracts/=node_modules/solady/src/",
"forge-std/=lib/forge-std/src/",
"solady/=node_modules/solady/"
],
"optimizer": {
"enabled": true,
"runs": 10000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_rewardToken","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_unclaimedRewardsReceiver","type":"address"},{"internalType":"address","name":"_unclaimedRewardsReceiverSetter","type":"address"},{"internalType":"address","name":"_distributorAdmin","type":"address"},{"internalType":"address","name":"_merkleRootSetter","type":"address"},{"internalType":"address","name":"_pauserRole","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"Distributor__DistributionDoesNotExist","type":"error"},{"inputs":[],"name":"Distributor__InvalidBatchClaimSize","type":"error"},{"inputs":[],"name":"Distributor__InvalidBatchSetMerkleRootSize","type":"error"},{"inputs":[],"name":"Distributor__InvalidMerkleRoot","type":"error"},{"inputs":[],"name":"Distributor__InvalidProof","type":"error"},{"inputs":[],"name":"Distributor__NewRewardAmountNotGreaterThanOrEqualClaimedAmount","type":"error"},{"inputs":[],"name":"Distributor__NoRewardsLeft","type":"error"},{"inputs":[],"name":"Distributor__NotPendingUnclaimedRewardsReceiver","type":"error"},{"inputs":[],"name":"Distributor__SameMerkleRoot","type":"error"},{"inputs":[],"name":"Distributor__SameUnclaimedRewardsReceiver","type":"error"},{"inputs":[],"name":"Distributor__UserHasAlreadyClaimed","type":"error"},{"inputs":[],"name":"Distributor__ZeroAddress","type":"error"},{"inputs":[],"name":"Distributor__ZeroAmount","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256[]","name":"_distributionIds","type":"uint256[]"},{"indexed":true,"internalType":"bytes32[]","name":"_merkleRoots","type":"bytes32[]"},{"indexed":true,"internalType":"uint256[]","name":"_rewardAmounts","type":"uint256[]"}],"name":"BatchDistributionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_distributionId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"_rewardAmount","type":"uint256"}],"name":"DistributionCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_distributionId","type":"uint256"},{"indexed":true,"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"indexed":true,"internalType":"uint256","name":"_rewardAmount","type":"uint256"}],"name":"DistributionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_rewardToken","type":"address"},{"indexed":true,"internalType":"address","name":"_unclaimedRewardsReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"Genesis","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_distributionId","type":"uint256"},{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"RewardTokenClaimed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":true,"internalType":"struct Distributor.ClaimInfo[]","name":"_claimInfos","type":"tuple[]"},{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"RewardTokenClaimedBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"RewardTokenPushed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"RewardTokenWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newUnclaimedRewardsReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"UnclaimedRewardsReceiverProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_oldUnclaimedRewardsReceiver","type":"address"},{"indexed":true,"internalType":"address","name":"_newUnclaimedRewardsReceiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"UnclaimedRewardsReceiverSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTOR_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MERKLE_ROOT_SETTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNCLAIMED_RECEIVER_SETTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptUnclaimedRewardsReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Distributor.ClaimInfo[]","name":"_claimInfos","type":"tuple[]"},{"internalType":"bytes32[][]","name":"_proofs","type":"bytes32[][]"}],"name":"batchClaimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_distributionIds","type":"uint256[]"},{"internalType":"bytes32[]","name":"_merkleRoots","type":"bytes32[]"},{"internalType":"uint256[]","name":"_newRewardAmounts","type":"uint256[]"}],"name":"batchSetMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct Distributor.ClaimInfo","name":"_claimInfos","type":"tuple"},{"internalType":"bytes32[]","name":"_proofs","type":"bytes32[]"}],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_rewardAmount","type":"uint256"}],"name":"createDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributionIdCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"}],"name":"distributionInfo","outputs":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_distributionId","type":"uint256"}],"name":"getDistributionInfo","outputs":[{"components":[{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"rewardAmount","type":"uint256"},{"internalType":"uint256","name":"claimedAmount","type":"uint256"}],"internalType":"struct Distributor.DistributionInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingUnclaimedRewardsReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newUnclaimedRewardsReceiver","type":"address"}],"name":"proposeUnclaimedRewardsReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_distributionId","type":"uint256"},{"internalType":"bytes32","name":"_merkleRoot","type":"bytes32"},{"internalType":"uint256","name":"_newRewardAmount","type":"uint256"}],"name":"setMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unclaimedRewardsReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"userClaims","outputs":[{"internalType":"bool","name":"isClaimed","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a060405234801561000f575f80fd5b5060405161258338038061258383398101604081905261002e9161027c565b6100378761018e565b6100408661018e565b6100498561018e565b6100528461018e565b61005b8361018e565b6100648261018e565b61006d8161018e565b6100775f876101b8565b506100a27f39eeb352e6efda471c1dc4a527da31e6fd6a138563d65ec20dc23cad03bfa704846101b8565b506100cd7f11c5c29eef9ee2a6bc611bdfd3bf580b41f6c07f5cda2c8580a8fe102b87dcb6856101b8565b506100f87fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed090474836101b8565b506101237f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a826101b8565b506001600160a01b03878116608081905260018054610100600160a81b0319166101009389169384021790556040514281527fd1f8ffbf4a62d8460135d84eea01645941d0d1123136fa69cad27edbdb6c2b0f9060200160405180910390a3505050505050506102fd565b6001600160a01b0381166101b55760405163cddcba4360e01b815260040160405180910390fd5b50565b5f828152602081815260408083206001600160a01b038516845290915281205460ff16610258575f838152602081815260408083206001600160a01b03861684529091529020805460ff191660011790556102103390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a450600161025b565b505f5b92915050565b80516001600160a01b0381168114610277575f80fd5b919050565b5f805f805f805f60e0888a031215610292575f80fd5b61029b88610261565b96506102a960208901610261565b95506102b760408901610261565b94506102c560608901610261565b93506102d360808901610261565b92506102e160a08901610261565b91506102ef60c08901610261565b905092959891949750929550565b6080516122606103235f395f81816103d401528181611006015261104c01526122605ff3fe608060405234801561000f575f80fd5b50600436106101c6575f3560e01c80638cc1ea62116100fe578063bda25a331161009e578063e08ed6221161006e578063e08ed622146104a9578063e63ab1e914610523578063ea55ba671461054a578063f9c0a3e91461055d575f80fd5b8063bda25a331461045d578063c84405f214610470578063d3099b3614610483578063d547741f14610496575f80fd5b806399b2f6a7116100d957806399b2f6a7146103f65780639e2dc50014610409578063a217fddf14610436578063ac9650d81461043d575f80fd5b80638cc1ea621461038457806391d148541461038c57806399248ea7146103cf575f80fd5b806336568abe116101695780634462736f116101445780634462736f146103055780635b6a26f61461032c5780635c975abb146103715780638456cb591461037c575f80fd5b806336568abe146102e15780633ca4272d146102f45780633f4ba83a146102fd575f80fd5b80632b6063a3116101a45780632b6063a3146102495780632e7a923f146102925780632f2ff15d146102a757806336272147146102ba575f80fd5b806301ffc9a7146101ca5780630e6fcd3a146101f2578063248a9ca314610227575b5f80fd5b6101dd6101d8366004611c1e565b610582565b60405190151581526020015b60405180910390f35b6102197f39eeb352e6efda471c1dc4a527da31e6fd6a138563d65ec20dc23cad03bfa70481565b6040519081526020016101e9565b610219610235366004611c5d565b5f9081526020819052604090206001015490565b610277610257366004611c5d565b60046020525f908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016101e9565b6102a56102a0366004611c74565b61061a565b005b6102a56102b5366004611cc5565b61072c565b6102197fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed09047481565b6102a56102ef366004611cc5565b610756565b61021960035481565b6102a56107b4565b6102197f11c5c29eef9ee2a6bc611bdfd3bf580b41f6c07f5cda2c8580a8fe102b87dcb681565b60025461034c9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e9565b60015460ff166101dd565b6102a56107e9565b6102a561081b565b6101dd61039a366004611cc5565b5f9182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b61034c7f000000000000000000000000000000000000000000000000000000000000000081565b6102a5610404366004611cef565b6108df565b6101dd610417366004611cc5565b600560209081525f928352604080842090915290825290205460ff1681565b6102195f81565b61045061044b366004611d57565b6109bb565b6040516101e99190611d96565b6102a561046b366004611e49565b610aa1565b6102a561047e366004611e9f565b610b14565b6102a5610491366004611f32565b610d0f565b6102a56104a4366004611cc5565b610df0565b6105016104b7366004611c5d565b60408051606080820183525f808352602080840182905292840181905293845260048252928290208251938401835280548452600181015491840191909152600201549082015290565b60408051825181526020808401519082015291810151908201526060016101e9565b6102197f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6102a5610558366004611fc4565b610e14565b60015461034c90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061061457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed09047461064481610eeb565b5f80610651868686610ef5565b9150915080821115610698576106673383610fec565b604051429083907f161377dd4812ed923665021c2dc3558ee50932fa4490ebe94c74706fa22c3571905f90a36106f5565b808210156106f5576001546106c890610100900473ffffffffffffffffffffffffffffffffffffffff1682611032565b604051429082907f72c818677a85f58c2690a5a070c3fff5d58d62ab37c15b3024e454820c66c8a8905f90a35b8385877f1e8dc45b3def8d1b9f7d795d7320dec6c92928573ee8592af236b79321b70f8760405160405180910390a4505050505050565b5f8281526020819052604090206001015461074681610eeb565b6107508383611073565b50505050565b73ffffffffffffffffffffffffffffffffffffffff811633146107a5576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107af828261116c565b505050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6107de81610eeb565b6107e6611225565b50565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61081381610eeb565b6107e66112a2565b6108236112fb565b600180546002805473ffffffffffffffffffffffffffffffffffffffff8181166101008181027fffffffffffffffffffffff0000000000000000000000000000000000000000ff8716179096557fffffffffffffffffffffffff0000000000000000000000000000000000000000909216909255604051939092041691819083907f09c5ff54fe45456b1d5f1a6d8c7693ba79c6bf5713adbed8bc321164dfb2a2cf906108d39042815260200190565b60405180910390a35050565b7f39eeb352e6efda471c1dc4a527da31e6fd6a138563d65ec20dc23cad03bfa70461090981610eeb565b61091283611364565b61091b8261139b565b5f60405180606001604052808581526020018481526020015f81525090505f60035f81546109489061200a565b91829055505f8181526004602090815260409182902085518155908501516001820155908401516002909101559050610985338360200151610fec565b8385827f2aaa9b4b94309089559f01ba50418517ff0eff08483c33b97aa2108b3121624a60405160405180910390a45050505050565b604080515f8152602081019091526060908267ffffffffffffffff8111156109e5576109e5612054565b604051908082528060200260200182016040528015610a1857816020015b6060815260200190600190039081610a035790505b5091505f5b83811015610a9957610a7430868684818110610a3b57610a3b612081565b9050602002810190610a4d91906120ae565b85604051602001610a6093929190612126565b6040516020818303038152906040526113d4565b838281518110610a8657610a86612081565b6020908102919091010152600101610a1d565b505092915050565b610aa9611453565b33610ab684848484611490565b50610ac5818560200135611032565b60405160208501359073ffffffffffffffffffffffffffffffffffffffff8316908635907f93bc18e04e83836a4588324e1bcf1177d10a353ea24a3812e2606ae01ea141c4905f90a450505050565b7fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed090474610b3e81610eeb565b610b49868584611667565b5f805f5b88811015610bd4575f80610baa8c8c85818110610b6c57610b6c612081565b905060200201358b8b86818110610b8557610b85612081565b905060200201358a8a87818110610b9e57610b9e612081565b90506020020135610ef5565b9092509050610bb98286612145565b9450610bc58185612145565b93505050806001019050610b4d565b5080821115610c2857610bf033610beb8385612041565b610fec565b42610bfb8284612041565b6040517f161377dd4812ed923665021c2dc3558ee50932fa4490ebe94c74706fa22c3571905f90a3610c95565b80821015610c9557600154610c6190610100900473ffffffffffffffffffffffffffffffffffffffff16610c5c8484612041565b611032565b42610c6c8383612041565b6040517f72c818677a85f58c2690a5a070c3fff5d58d62ab37c15b3024e454820c66c8a8905f90a35b8484604051610ca5929190612198565b60405180910390208787604051610cbd929190612198565b60405180910390208a8a604051610cd5929190612198565b604051908190038120907f4db72823df8cb65233bb80ad2999f0a2b41fc429196bfb746bb437167757b0fd905f90a4505050505050505050565b610d17611453565b610d2183826116ad565b335f805b85811015610d8657610d72878783818110610d4257610d42612081565b905060400201868684818110610d5a57610d5a612081565b9050602002810190610d6c91906121ac565b86611490565b610d7c9083612145565b9150600101610d25565b50610d918282611032565b808273ffffffffffffffffffffffffffffffffffffffff168787604051610db9929190612210565b604051908190038120907ff79bd659fb8085c72871285995d889f54f5a052c322b7addd2a67935f1f2e966905f90a4505050505050565b5f82815260208190526040902060010154610e0a81610eeb565b610750838361116c565b7f11c5c29eef9ee2a6bc611bdfd3bf580b41f6c07f5cda2c8580a8fe102b87dcb6610e3e81610eeb565b610e47826116e6565b610e718260018054906101000a900473ffffffffffffffffffffffffffffffffffffffff16611733565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040514281527f3e84a74a77a1891a9ce31d083e2e6d481c2bf59b32d9f986327958cb7434ef8c9060200160405180910390a25050565b6107e68133611798565b5f80610f0084611364565b610f098361139b565b5f858152600460209081526040918290208251606081018452815481526001820154928101929092526002015491810191909152610f4681611822565b610f5385825f015161185a565b610f61848260400151611893565b604080516060810182528681526020808201878152848401518385019081525f8b815260048452949094208351815590516001820155925160029093019290925590820151851115610fc3576020820151610fbc9086612041565b9350610fe2565b8160200151851015610fe257848260200151610fdf9190612041565b92505b5050935093915050565b61102e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168330846118cd565b5050565b61102e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168383611956565b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16611165575f8381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556111033390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610614565b505f610614565b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615611165575f8381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610614565b61122d611994565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6112aa611453565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611278565b60025473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611362576040517f15afb59700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b806107e6576040517ff7a1594000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f036107e6576040517f30ca019e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60605f808473ffffffffffffffffffffffffffffffffffffffff16846040516113fd9190612248565b5f60405180830381855af49150503d805f8114611435576040519150601f19603f3d011682016040523d82523d5f602084013e61143a565b606091505b509150915061144a8583836119d0565b95945050505050565b60015460ff1615611362576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61149e856020013561139b565b84355f9081526004602090815260409182902082516060810184528154815260018201549281019290925260020154918101919091526114dd81611822565b6114fe816040015182602001516114f49190612041565b8760200135611a62565b611509863584611a9c565b6040805173ffffffffffffffffffffffffffffffffffffffff8516602080830191909152888101358284015288356060808401919091528351808403909101815260808301845280519082012060a08084018290528451808503909101815260c090930190935281519101208251611585908890889084611b05565b6115bb576040517f0d0fc02800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87355f90815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8916845282529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559084018051918a01359161162a908390612145565b905250505085355f908152600460209081526040918290208351815581840151600182015591909201516002909101558501359050949350505050565b81831415806116765750808314155b156107af576040517fbde4c23f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80821461102e576040517f06bc7f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166107e6576040517fcddcba4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361102e576040517fbc107d0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661102e576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602481018390526044015b60405180910390fd5b80516107e6576040517fbbf42cdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80820361102e576040517f34d5c95f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8082101561102e576040517fc836331e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff84811660248301528381166044830152606482018390526107509186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611b3d565b60405173ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018390526107af91859182169063a9059cbb9060640161190f565b60015460ff16611362576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060826119e5576119e082611bdc565b611a5b565b8151158015611a09575073ffffffffffffffffffffffffffffffffffffffff84163b155b15611a58576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401611819565b50805b9392505050565b8082101561102e576040517f9bae088e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f82815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff161561102e576040517f5add517700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8315611b35578360051b8501855b803580851160051b94855260209485185260405f209301818110611b145750505b501492915050565b5f8060205f8451602086015f885af180611b5c576040513d5f823e3d81fd5b50505f513d91508115611b73578060011415611b8d565b73ffffffffffffffffffffffffffffffffffffffff84163b155b15610750576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401611819565b805115611bec5780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60208284031215611c2e575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611a5b575f80fd5b5f60208284031215611c6d575f80fd5b5035919050565b5f805f60608486031215611c86575f80fd5b505081359360208301359350604090920135919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611cc0575f80fd5b919050565b5f8060408385031215611cd6575f80fd5b82359150611ce660208401611c9d565b90509250929050565b5f8060408385031215611d00575f80fd5b50508035926020909101359150565b5f8083601f840112611d1f575f80fd5b50813567ffffffffffffffff811115611d36575f80fd5b6020830191508360208260051b8501011115611d50575f80fd5b9250929050565b5f8060208385031215611d68575f80fd5b823567ffffffffffffffff811115611d7e575f80fd5b611d8a85828601611d0f565b90969095509350505050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015611e3c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845281518051808752808883018989015e5f878201890152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909501860194509285019290850190600101611dbb565b5092979650505050505050565b5f805f8385036060811215611e5c575f80fd5b6040811215611e69575f80fd5b50839250604084013567ffffffffffffffff811115611e86575f80fd5b611e9286828701611d0f565b9497909650939450505050565b5f805f805f8060608789031215611eb4575f80fd5b863567ffffffffffffffff80821115611ecb575f80fd5b611ed78a838b01611d0f565b90985096506020890135915080821115611eef575f80fd5b611efb8a838b01611d0f565b90965094506040890135915080821115611f13575f80fd5b50611f2089828a01611d0f565b979a9699509497509295939492505050565b5f805f8060408587031215611f45575f80fd5b843567ffffffffffffffff80821115611f5c575f80fd5b818701915087601f830112611f6f575f80fd5b813581811115611f7d575f80fd5b8860208260061b8501011115611f91575f80fd5b602092830196509450908601359080821115611fab575f80fd5b50611fb887828801611d0f565b95989497509550505050565b5f60208284031215611fd4575f80fd5b611a5b82611c9d565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361203a5761203a611fdd565b5060010190565b8181038181111561061457610614611fdd565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126120e1575f80fd5b83018035915067ffffffffffffffff8211156120fb575f80fd5b602001915036819003821315611d50575f80fd5b5f81518060208401855e5f93019283525090919050565b828482375f8382015f815261213b818561210f565b9695505050505050565b8082018082111561061457610614611fdd565b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612185575f80fd5b8260051b80838637939093019392505050565b5f6121a4828486612158565b949350505050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126121df575f80fd5b83018035915067ffffffffffffffff8211156121f9575f80fd5b6020019150600581901b3603821315611d50575f80fd5b5f8184825b8581101561223d57813583526020808301359084015260409283019290910190600101612215565b509095945050505050565b5f611a5b828461210f56fea164736f6c6343000819000a00000000000000000000000055555555555555555555555555555555555555550000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Deployed Bytecode
0x608060405234801561000f575f80fd5b50600436106101c6575f3560e01c80638cc1ea62116100fe578063bda25a331161009e578063e08ed6221161006e578063e08ed622146104a9578063e63ab1e914610523578063ea55ba671461054a578063f9c0a3e91461055d575f80fd5b8063bda25a331461045d578063c84405f214610470578063d3099b3614610483578063d547741f14610496575f80fd5b806399b2f6a7116100d957806399b2f6a7146103f65780639e2dc50014610409578063a217fddf14610436578063ac9650d81461043d575f80fd5b80638cc1ea621461038457806391d148541461038c57806399248ea7146103cf575f80fd5b806336568abe116101695780634462736f116101445780634462736f146103055780635b6a26f61461032c5780635c975abb146103715780638456cb591461037c575f80fd5b806336568abe146102e15780633ca4272d146102f45780633f4ba83a146102fd575f80fd5b80632b6063a3116101a45780632b6063a3146102495780632e7a923f146102925780632f2ff15d146102a757806336272147146102ba575f80fd5b806301ffc9a7146101ca5780630e6fcd3a146101f2578063248a9ca314610227575b5f80fd5b6101dd6101d8366004611c1e565b610582565b60405190151581526020015b60405180910390f35b6102197f39eeb352e6efda471c1dc4a527da31e6fd6a138563d65ec20dc23cad03bfa70481565b6040519081526020016101e9565b610219610235366004611c5d565b5f9081526020819052604090206001015490565b610277610257366004611c5d565b60046020525f908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016101e9565b6102a56102a0366004611c74565b61061a565b005b6102a56102b5366004611cc5565b61072c565b6102197fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed09047481565b6102a56102ef366004611cc5565b610756565b61021960035481565b6102a56107b4565b6102197f11c5c29eef9ee2a6bc611bdfd3bf580b41f6c07f5cda2c8580a8fe102b87dcb681565b60025461034c9073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101e9565b60015460ff166101dd565b6102a56107e9565b6102a561081b565b6101dd61039a366004611cc5565b5f9182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b61034c7f000000000000000000000000555555555555555555555555555555555555555581565b6102a5610404366004611cef565b6108df565b6101dd610417366004611cc5565b600560209081525f928352604080842090915290825290205460ff1681565b6102195f81565b61045061044b366004611d57565b6109bb565b6040516101e99190611d96565b6102a561046b366004611e49565b610aa1565b6102a561047e366004611e9f565b610b14565b6102a5610491366004611f32565b610d0f565b6102a56104a4366004611cc5565b610df0565b6105016104b7366004611c5d565b60408051606080820183525f808352602080840182905292840181905293845260048252928290208251938401835280548452600181015491840191909152600201549082015290565b60408051825181526020808401519082015291810151908201526060016101e9565b6102197f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6102a5610558366004611fc4565b610e14565b60015461034c90610100900473ffffffffffffffffffffffffffffffffffffffff1681565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b00000000000000000000000000000000000000000000000000000000148061061457507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b7fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed09047461064481610eeb565b5f80610651868686610ef5565b9150915080821115610698576106673383610fec565b604051429083907f161377dd4812ed923665021c2dc3558ee50932fa4490ebe94c74706fa22c3571905f90a36106f5565b808210156106f5576001546106c890610100900473ffffffffffffffffffffffffffffffffffffffff1682611032565b604051429082907f72c818677a85f58c2690a5a070c3fff5d58d62ab37c15b3024e454820c66c8a8905f90a35b8385877f1e8dc45b3def8d1b9f7d795d7320dec6c92928573ee8592af236b79321b70f8760405160405180910390a4505050505050565b5f8281526020819052604090206001015461074681610eeb565b6107508383611073565b50505050565b73ffffffffffffffffffffffffffffffffffffffff811633146107a5576040517f6697b23200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107af828261116c565b505050565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a6107de81610eeb565b6107e6611225565b50565b7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a61081381610eeb565b6107e66112a2565b6108236112fb565b600180546002805473ffffffffffffffffffffffffffffffffffffffff8181166101008181027fffffffffffffffffffffff0000000000000000000000000000000000000000ff8716179096557fffffffffffffffffffffffff0000000000000000000000000000000000000000909216909255604051939092041691819083907f09c5ff54fe45456b1d5f1a6d8c7693ba79c6bf5713adbed8bc321164dfb2a2cf906108d39042815260200190565b60405180910390a35050565b7f39eeb352e6efda471c1dc4a527da31e6fd6a138563d65ec20dc23cad03bfa70461090981610eeb565b61091283611364565b61091b8261139b565b5f60405180606001604052808581526020018481526020015f81525090505f60035f81546109489061200a565b91829055505f8181526004602090815260409182902085518155908501516001820155908401516002909101559050610985338360200151610fec565b8385827f2aaa9b4b94309089559f01ba50418517ff0eff08483c33b97aa2108b3121624a60405160405180910390a45050505050565b604080515f8152602081019091526060908267ffffffffffffffff8111156109e5576109e5612054565b604051908082528060200260200182016040528015610a1857816020015b6060815260200190600190039081610a035790505b5091505f5b83811015610a9957610a7430868684818110610a3b57610a3b612081565b9050602002810190610a4d91906120ae565b85604051602001610a6093929190612126565b6040516020818303038152906040526113d4565b838281518110610a8657610a86612081565b6020908102919091010152600101610a1d565b505092915050565b610aa9611453565b33610ab684848484611490565b50610ac5818560200135611032565b60405160208501359073ffffffffffffffffffffffffffffffffffffffff8316908635907f93bc18e04e83836a4588324e1bcf1177d10a353ea24a3812e2606ae01ea141c4905f90a450505050565b7fc0bf15dbb0f0734a6d6a9d4c228810b2546966255459f10a1ac1b4ceed090474610b3e81610eeb565b610b49868584611667565b5f805f5b88811015610bd4575f80610baa8c8c85818110610b6c57610b6c612081565b905060200201358b8b86818110610b8557610b85612081565b905060200201358a8a87818110610b9e57610b9e612081565b90506020020135610ef5565b9092509050610bb98286612145565b9450610bc58185612145565b93505050806001019050610b4d565b5080821115610c2857610bf033610beb8385612041565b610fec565b42610bfb8284612041565b6040517f161377dd4812ed923665021c2dc3558ee50932fa4490ebe94c74706fa22c3571905f90a3610c95565b80821015610c9557600154610c6190610100900473ffffffffffffffffffffffffffffffffffffffff16610c5c8484612041565b611032565b42610c6c8383612041565b6040517f72c818677a85f58c2690a5a070c3fff5d58d62ab37c15b3024e454820c66c8a8905f90a35b8484604051610ca5929190612198565b60405180910390208787604051610cbd929190612198565b60405180910390208a8a604051610cd5929190612198565b604051908190038120907f4db72823df8cb65233bb80ad2999f0a2b41fc429196bfb746bb437167757b0fd905f90a4505050505050505050565b610d17611453565b610d2183826116ad565b335f805b85811015610d8657610d72878783818110610d4257610d42612081565b905060400201868684818110610d5a57610d5a612081565b9050602002810190610d6c91906121ac565b86611490565b610d7c9083612145565b9150600101610d25565b50610d918282611032565b808273ffffffffffffffffffffffffffffffffffffffff168787604051610db9929190612210565b604051908190038120907ff79bd659fb8085c72871285995d889f54f5a052c322b7addd2a67935f1f2e966905f90a4505050505050565b5f82815260208190526040902060010154610e0a81610eeb565b610750838361116c565b7f11c5c29eef9ee2a6bc611bdfd3bf580b41f6c07f5cda2c8580a8fe102b87dcb6610e3e81610eeb565b610e47826116e6565b610e718260018054906101000a900473ffffffffffffffffffffffffffffffffffffffff16611733565b600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff84169081179091556040514281527f3e84a74a77a1891a9ce31d083e2e6d481c2bf59b32d9f986327958cb7434ef8c9060200160405180910390a25050565b6107e68133611798565b5f80610f0084611364565b610f098361139b565b5f858152600460209081526040918290208251606081018452815481526001820154928101929092526002015491810191909152610f4681611822565b610f5385825f015161185a565b610f61848260400151611893565b604080516060810182528681526020808201878152848401518385019081525f8b815260048452949094208351815590516001820155925160029093019290925590820151851115610fc3576020820151610fbc9086612041565b9350610fe2565b8160200151851015610fe257848260200151610fdf9190612041565b92505b5050935093915050565b61102e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005555555555555555555555555555555555555555168330846118cd565b5050565b61102e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000005555555555555555555555555555555555555555168383611956565b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff16611165575f8381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff86168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556111033390565b73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610614565b505f610614565b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915281205460ff1615611165575f8381526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610614565b61122d611994565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390a1565b6112aa611453565b600180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016811790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833611278565b60025473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611362576040517f15afb59700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b806107e6576040517ff7a1594000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f036107e6576040517f30ca019e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60605f808473ffffffffffffffffffffffffffffffffffffffff16846040516113fd9190612248565b5f60405180830381855af49150503d805f8114611435576040519150601f19603f3d011682016040523d82523d5f602084013e61143a565b606091505b509150915061144a8583836119d0565b95945050505050565b60015460ff1615611362576040517fd93c066500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f61149e856020013561139b565b84355f9081526004602090815260409182902082516060810184528154815260018201549281019290925260020154918101919091526114dd81611822565b6114fe816040015182602001516114f49190612041565b8760200135611a62565b611509863584611a9c565b6040805173ffffffffffffffffffffffffffffffffffffffff8516602080830191909152888101358284015288356060808401919091528351808403909101815260808301845280519082012060a08084018290528451808503909101815260c090930190935281519101208251611585908890889084611b05565b6115bb576040517f0d0fc02800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b87355f90815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8916845282529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559084018051918a01359161162a908390612145565b905250505085355f908152600460209081526040918290208351815581840151600182015591909201516002909101558501359050949350505050565b81831415806116765750808314155b156107af576040517fbde4c23f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80821461102e576040517f06bc7f6800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166107e6576040517fcddcba4300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361102e576040517fbc107d0f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff1661102e576040517fe2517d3f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82166004820152602481018390526044015b60405180910390fd5b80516107e6576040517fbbf42cdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80820361102e576040517f34d5c95f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8082101561102e576040517fc836331e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405173ffffffffffffffffffffffffffffffffffffffff84811660248301528381166044830152606482018390526107509186918216906323b872dd906084015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611b3d565b60405173ffffffffffffffffffffffffffffffffffffffff8381166024830152604482018390526107af91859182169063a9059cbb9060640161190f565b60015460ff16611362576040517f8dfc202b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6060826119e5576119e082611bdc565b611a5b565b8151158015611a09575073ffffffffffffffffffffffffffffffffffffffff84163b155b15611a58576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401611819565b50805b9392505050565b8082101561102e576040517f9bae088e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f82815260056020908152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff161561102e576040517f5add517700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f8315611b35578360051b8501855b803580851160051b94855260209485185260405f209301818110611b145750505b501492915050565b5f8060205f8451602086015f885af180611b5c576040513d5f823e3d81fd5b50505f513d91508115611b73578060011415611b8d565b73ffffffffffffffffffffffffffffffffffffffff84163b155b15610750576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152602401611819565b805115611bec5780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60208284031215611c2e575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611a5b575f80fd5b5f60208284031215611c6d575f80fd5b5035919050565b5f805f60608486031215611c86575f80fd5b505081359360208301359350604090920135919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114611cc0575f80fd5b919050565b5f8060408385031215611cd6575f80fd5b82359150611ce660208401611c9d565b90509250929050565b5f8060408385031215611d00575f80fd5b50508035926020909101359150565b5f8083601f840112611d1f575f80fd5b50813567ffffffffffffffff811115611d36575f80fd5b6020830191508360208260051b8501011115611d50575f80fd5b9250929050565b5f8060208385031215611d68575f80fd5b823567ffffffffffffffff811115611d7e575f80fd5b611d8a85828601611d0f565b90969095509350505050565b5f602080830181845280855180835260408601915060408160051b87010192508387015f5b82811015611e3c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845281518051808752808883018989015e5f878201890152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909501860194509285019290850190600101611dbb565b5092979650505050505050565b5f805f8385036060811215611e5c575f80fd5b6040811215611e69575f80fd5b50839250604084013567ffffffffffffffff811115611e86575f80fd5b611e9286828701611d0f565b9497909650939450505050565b5f805f805f8060608789031215611eb4575f80fd5b863567ffffffffffffffff80821115611ecb575f80fd5b611ed78a838b01611d0f565b90985096506020890135915080821115611eef575f80fd5b611efb8a838b01611d0f565b90965094506040890135915080821115611f13575f80fd5b50611f2089828a01611d0f565b979a9699509497509295939492505050565b5f805f8060408587031215611f45575f80fd5b843567ffffffffffffffff80821115611f5c575f80fd5b818701915087601f830112611f6f575f80fd5b813581811115611f7d575f80fd5b8860208260061b8501011115611f91575f80fd5b602092830196509450908601359080821115611fab575f80fd5b50611fb887828801611d0f565b95989497509550505050565b5f60208284031215611fd4575f80fd5b611a5b82611c9d565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361203a5761203a611fdd565b5060010190565b8181038181111561061457610614611fdd565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126120e1575f80fd5b83018035915067ffffffffffffffff8211156120fb575f80fd5b602001915036819003821315611d50575f80fd5b5f81518060208401855e5f93019283525090919050565b828482375f8382015f815261213b818561210f565b9695505050505050565b8082018082111561061457610614611fdd565b5f7f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612185575f80fd5b8260051b80838637939093019392505050565b5f6121a4828486612158565b949350505050565b5f8083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126121df575f80fd5b83018035915067ffffffffffffffff8211156121f9575f80fd5b6020019150600581901b3603821315611d50575f80fd5b5f8184825b8581101561223d57813583526020808301359084015260409283019290910190600101612215565b509095945050505050565b5f611a5b828461210f56fea164736f6c6343000819000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000055555555555555555555555555555555555555550000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec90000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
-----Decoded View---------------
Arg [0] : _rewardToken (address): 0x5555555555555555555555555555555555555555
Arg [1] : _admin (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
Arg [2] : _unclaimedRewardsReceiver (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
Arg [3] : _unclaimedRewardsReceiverSetter (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
Arg [4] : _distributorAdmin (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
Arg [5] : _merkleRootSetter (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
Arg [6] : _pauserRole (address): 0x2157f54f7a745c772e686AA691Fa590B49171eC9
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000005555555555555555555555555555555555555555
Arg [1] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Arg [2] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Arg [3] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Arg [4] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Arg [5] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Arg [6] : 0000000000000000000000002157f54f7a745c772e686aa691fa590b49171ec9
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$44,083.11
Net Worth in HYPE
Token Allocations
WHYPE
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| HYPEREVM | 100.00% | $23.49 | 1,876.6755 | $44,083.11 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.