Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"@openzeppelin/contracts/access/Ownable2Step.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
"
},
"@openzeppelin/contracts/security/Pausable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../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 {
/**
* @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);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @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 {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @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());
}
}
"
},
"@openzeppelin/contracts/security/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
"
},
"@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
"
},
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @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;
}
}
"
},
"contracts/HYMToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
/**
*
* @title HYMToken - HydroMine project's main token
* @dev ERC20 token with fees and integration with external contracts (Vault and Distribution)
* @notice Implemented fixes:
* - Fixed constructor (removed duplicate code)
* - Simplified whitelist logic
* - Optimized _update function
* - Prepared for Gnosis Safe
*/
import '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import '@openzeppelin/contracts/access/Ownable2Step.sol';
import '@openzeppelin/contracts/security/Pausable.sol';
import '@openzeppelin/contracts/security/ReentrancyGuard.sol';
interface IHolderDistributor {
function notifyReceived(address holder) external;
function isEligible(address holder) external view returns (bool);
function pendingDistribution(address holder) external view returns (uint256);
}
interface IHYMStaking {
function isEligibleForFeeExemption(address user) external view returns (bool);
}
contract HYMToken is ERC20, Ownable2Step, Pausable, ReentrancyGuard {
// --- Timelock for Gnosis Safe ---
address public timelock;
event TimelockSet(address indexed oldTimelock, address indexed newTimelock);
modifier onlyGov() {
require(msg.sender == owner() || msg.sender == timelock, 'Only owner/timelock');
_;
}
// --- Fees (base 1,000,000) ---
uint256 public burnFee = 50; // 0.005% - automatic burn
uint256 public marketingFee = 20000; // 2% - marketing and development
uint256 public holderFee = 9500; // 0.95% - redistribution to holders
uint256 public vaultFee = 30000; // 3% - sent to the Redistribution Vault
uint256 public constant FEE_DENOMINATOR = 1_000_000; // Base for percentage calculation
// --- Contract addresses ---
address public marketingWallet;
address public holderDistributor;
address public vaultAddress;
address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
// --- Security rules ---
uint256 public minHolderBalance = 10_000 * 10 ** decimals();
uint256 public lastFeeUpdate;
mapping(address => uint256) public lastTxBlock;
mapping(address => bool) public isBlocked;
mapping(address => bool) public isFeeExempt; // Addresses exempt from fees
// --- Router management (SIMPLIFIED) ---
mapping(address => bool) public isWhitelistedRouter; // DEXs, bridges with fee exemption
mapping(address => bool) public isBlacklistedRouter; // DEXs, bridges blocked
bool public antiBotEnabled = false;
// --- External contracts ---
IHYMStaking public stakingContract;
// --- Events ---
event FeesUpdated(uint256 burn, uint256 marketing, uint256 holder, uint256 vault);
event WalletsUpdated(address marketing, address distributor, address vault);
event MinBalanceUpdated(uint256 minBalance);
event TransferWithFee(
address indexed from,
address indexed to,
uint256 value,
uint256 burn,
uint256 marketing,
uint256 holders,
uint256 vault
);
event AddressBlocked(address indexed account, bool blocked);
event FeeExemptionSet(address indexed account, bool exempt);
event AntiBotStatusUpdated(bool enabled);
event RouterWhitelistUpdated(address indexed router, bool whitelisted);
event RouterBlacklistUpdated(address indexed router, bool blacklisted);
// --- Modifiers ---
/**
* @dev Ensures that the sum of the fees does not exceed 100%.
*/
modifier validFees(
uint256 _burn,
uint256 _marketing,
uint256 _holder,
uint256 _vault
) {
require(_burn + _marketing + _holder + _vault <= FEE_DENOMINATOR, 'Total fee > 100%');
_;
}
/**
* @dev Prevents rates from changing too frequently.
*
*/
modifier cooldown() {
// require(block.timestamp > lastFeeUpdate + 1 days, 'Cooldown: wait before changing fees again');
_;
lastFeeUpdate = block.timestamp;
}
/**
* @dev Constructor for the HYMToken contract
* @param _marketingWallet Address of the marketing wallet.
* @param _holderDistributor Address of the holder distributor contract.
* @param _vaultAddress Address of the vault.
*/
constructor(address _marketingWallet, address _holderDistributor, address _vaultAddress)
ERC20("HydroMine", "HYM")
Ownable2Step()
{
require(_marketingWallet != address(0), "Invalid marketing wallet");
require(_holderDistributor != address(0), "Invalid holder distributor");
require(_vaultAddress != address(0), "Invalid vault address");
marketingWallet = _marketingWallet;
holderDistributor = _holderDistributor;
vaultAddress = _vaultAddress;
lastFeeUpdate = block.timestamp - 1 days;
// Mint total supply (210 million)
_mint(msg.sender, 210_000_000 * 10 ** decimals());
// Exempt critical addresses from fees
isFeeExempt[BURN_ADDRESS] = true;
isFeeExempt[address(this)] = true;
isFeeExempt[msg.sender] = true; // Owner of the contract
}
/**
* @notice Defines the timelock address for decentralized governance (Gnosis Safe)
* @param newTimelock The address of the new timelock
*/
function setTimelock(address newTimelock) public onlyOwner {
emit TimelockSet(timelock, newTimelock);
timelock = newTimelock;
}
/**
* @notice Defines the address of the staking contract.
* @param _staking The address of the HYMStaking contract.
*/
function setStakingContract(address _staking) external onlyOwner {
stakingContract = IHYMStaking(_staking);
}
/**
* @notice Checks if a user is eligible for a fee waiver.
* @param user The address of the user to check.
* @return True if the user is eligible for a fee waiver, false otherwise.
*/
function isEligibleHolder(address user) public view returns (bool) {
if (isFeeExempt[user]) return true;
if (address(stakingContract) == address(0)) return false;
return stakingContract.isEligibleForFeeExemption(user);
}
/**
* @dev Transfer logic with taxation and redistribution (COMPATIBLE WITH OZ 4.9)
* @param from The address sending the tokens.
* @param to The address receiving the tokens.
* @param amount The amount of tokens to be transferred.
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal override {
super._beforeTokenTransfer(from, to, amount);
// Checks only for transfers (not mint/burn)
if (from != address(0) && to != address(0)) {
// Check if the token is paused
require(!paused(), 'Token paused');
// Check blocked addresses
require(!isBlocked[from] && !isBlocked[to], 'Address is blocked');
// Check blacklisted routers
require(!isBlacklistedRouter[from] && !isBlacklistedRouter[to], "Router blacklisted");
// Anti-bot: prevent same-block transactions
if (antiBotEnabled) {
require(lastTxBlock[from] < block.number, 'No same-block transactions');
lastTxBlock[from] = block.number;
}
}
}
/**
* @dev Override the transfer function to apply fees
*/
function transfer(address to, uint256 amount) public override returns (bool) {
address owner = _msgSender();
_transferWithFees(owner, to, amount);
return true;
}
/**
* @dev Override the transferFrom function to apply fees
*/
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transferWithFees(from, to, amount);
return true;
}
/**
* @dev Internal function for transfers with fees
*/
function _transferWithFees(address from, address to, uint256 value) internal {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
// SIMPLIFIED WHITELIST LOGIC
// If sender or recipient is in the whitelist, do not apply fees
if (isWhitelistedRouter[from] || isWhitelistedRouter[to] || isEligibleHolder(from)) {
_transfer(from, to, value);
emit TransferWithFee(from, to, value, 0, 0, 0, 0);
_notifyDistributor(to);
return;
}
// Apply fees for regular transfers
uint256 burnAmt = (value * burnFee) / FEE_DENOMINATOR;
uint256 marketingAmt = (value * marketingFee) / FEE_DENOMINATOR;
uint256 holderAmt = (value * holderFee) / FEE_DENOMINATOR;
uint256 vaultAmt = (value * vaultFee) / FEE_DENOMINATOR;
uint256 transferAmt = value - burnAmt - marketingAmt - holderAmt - vaultAmt;
// Execute transfers
if (burnAmt > 0) _transfer(from, BURN_ADDRESS, burnAmt);
if (marketingAmt > 0) _transfer(from, marketingWallet, marketingAmt);
if (holderAmt > 0) _transfer(from, holderDistributor, holderAmt);
if (vaultAmt > 0) _transfer(from, vaultAddress, vaultAmt);
if (transferAmt > 0) _transfer(from, to, transferAmt);
// Notify distributor if necessary
_notifyDistributor(to);
emit TransferWithFee(from, to, value, burnAmt, marketingAmt, holderAmt, vaultAmt);
}
/**
* @dev Auxiliary function to notify the distributor
* @param recipient The address of the recipient
*/
function _notifyDistributor(address recipient) private {
if (recipient != address(0) && balanceOf(recipient) >= minHolderBalance) {
try IHolderDistributor(holderDistributor).notifyReceived(recipient) {} catch {}
}
}
// --- Configuration Functions ---
/**
* @notice Manage router whitelist (DEXs, bridges)
* @param router The address of the router
* @param whitelisted True to add to whitelist, false to remove
*/
function setRouterWhitelist(address router, bool whitelisted) external onlyGov {
require(router != address(0), "Invalid router address");
isWhitelistedRouter[router] = whitelisted;
emit RouterWhitelistUpdated(router, whitelisted);
}
/**
* @notice Manage router blacklist (DEXs, bridges)
* @param router The address of the router
* @param blacklisted True to add to blacklist, false to remove
*/
function setRouterBlacklist(address router, bool blacklisted) external onlyGov {
require(router != address(0), "Invalid router address");
isBlacklistedRouter[router] = blacklisted;
emit RouterBlacklistUpdated(router, blacklisted);
}
/**
* @notice Update the marketing wallet address
* @param _marketingWallet The new marketing wallet address.
*/
function setMarketingWallet(address _marketingWallet) external onlyGov {
require(_marketingWallet != address(0), "Invalid marketing wallet");
marketingWallet = _marketingWallet;
emit WalletsUpdated(marketingWallet, holderDistributor, vaultAddress);
}
/**
* @notice Update the holder distributor contract address
* @param _holderDistributor The new holder distributor contract address.
*/
function setHolderDistributor(address _holderDistributor) external onlyGov {
require(_holderDistributor != address(0), "Invalid holder distributor");
holderDistributor = _holderDistributor;
emit WalletsUpdated(marketingWallet, holderDistributor, vaultAddress);
}
/**
* @notice Update the vault address
* @param _vaultAddress The new vault address.
*/
function setVaultAddress(address _vaultAddress) external onlyGov {
require(_vaultAddress != address(0), "Invalid vault address");
vaultAddress = _vaultAddress;
emit WalletsUpdated(marketingWallet, holderDistributor, vaultAddress);
}
/**
* @notice Enable or disable the anti-bot mechanism.
* @param enabled True to enable, false to disable.
*/
function setAntiBotEnabled(bool enabled) external onlyGov {
antiBotEnabled = enabled;
emit AntiBotStatusUpdated(enabled);
}
/**
* @notice Update the redistribution fees.
* @param _burn The new burn fee.
* @param _marketing The new marketing fee.
* @param _holder The new holder fee.
* @param _vault The new vault fee.
*/
function updateFees(
uint256 _burn,
uint256 _marketing,
uint256 _holder,
uint256 _vault
) external onlyOwner validFees(_burn, _marketing, _holder, _vault) cooldown {
burnFee = _burn;
marketingFee = _marketing;
holderFee = _holder;
vaultFee = _vault;
emit FeesUpdated(_burn, _marketing, _holder, _vault);
}
/**
* @notice Update the addresses of the marketing, distributor, and vault wallets.
* @param _marketing The new marketing wallet address.
* @param _distributor The new holder distributor contract address.
* @param _vault The new vault address.
*/
function updateWallets(
address _marketing,
address _distributor,
address _vault
) external onlyOwner {
require(_marketing != address(0), 'Invalid marketing wallet');
require(_distributor != address(0), 'Invalid distributor');
require(_vault != address(0), 'Invalid vault address');
marketingWallet = _marketing;
holderDistributor = _distributor;
vaultAddress = _vault;
emit WalletsUpdated(_marketing, _distributor, _vault);
}
/**
* @notice Update the minimum balance for a holder to be considered eligible.
* @param _minBalance The new minimum balance.
*/
function updateMinHolderBalance(uint256 _minBalance) external onlyOwner {
minHolderBalance = _minBalance;
emit MinBalanceUpdated(_minBalance);
}
/**
* @notice Pause token transfers.
*/
function pause() external onlyGov {
_pause();
}
/**
* @notice Unpause token transfers.
*/
function unpause() external onlyGov {
_unpause();
}
/**
* @notice Block or unblock an address.
* @param account The address to be blocked/unblocked.
* @param value True to block, false to unblock.
*/
function setBlocked(address account, bool value) external onlyGov {
isBlocked[account] = value;
emit AddressBlocked(account, value);
}
/**
* @notice Defines the tax exemption for a specific address.
* @param account The address to be exempt/not exempt.
* @param exempt True to exempt, false to not exempt.
*/
function setFeeExempt(address account, bool exempt) external onlyOwner {
isFeeExempt[account] = exempt;
emit FeeExemptionSet(account, exempt);
}
/**
* @notice Check the eligibility of a holder for distribution.
* @param holder The address of the holder.
* @return True if the holder is eligible, false otherwise.
*/
function checkHolderEligibility(address holder) external view returns (bool) {
return IHolderDistributor(holderDistributor).isEligible(holder);
}
/**
* @notice Returns the pending distribution for a holder.
* @param holder The address of the holder.
* @return The amount of tokens pending for distribution.
*/
function getHolderPendingDistribution(address holder) external view returns (uint256) {
return IHolderDistributor(holderDistributor).pendingDistribution(holder);
}
/**
* @notice Emergency function to recover accidentally sent ERC20 tokens
* @param token The address of the token to be recovered
* @param amount The amount to be recovered
*/
function emergencyRecoverToken(address token, uint256 amount) external onlyOwner {
require(token != address(this), "Cannot recover HYM");
require(token != address(0), "Invalid token address");
IERC20(token).transfer(owner(), amount);
}
}
"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-09-25 10:21:44
Comments
Log in to comment.
No comments yet.