HooxTokenV3
0x8ba7765fcccc69fab20538b5bcb6a1c1392e1234
Verification
Unverified
v0.8.26+commit.8a97fa7a
Type
Contract
5,271 bytes
ABI entries
32
17 read · 6 write
License
none
Contract information
- Address
- 0x8ba7765fcccc69fab20538b5bcb6a1c1392e1234
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x1F4B1595eb…44bbd0ACD3
- Creation tx
- 0x62c67c01d5…e0c13087d2
Token
- Name
- test
- Symbol
- TEST
- Decimals
- 18
- Holders
- 5
- Market
- View token page →
Read contract (17)
allowance(address, address) → uint256
balanceOf(address) → uint256
decimals() → uint8
eligibleSupply() → uint256
magnifiedRewardPerShare() → uint256
name() → string
rewardAsset() → address
rewardConfigurator() → address
rewardDistributor() → address
rewardExcluded(address) → bool
rewardsConfigured() → bool
symbol() → string
tokenURI() → string
totalRewardsDistributed() → uint256
totalSupply() → uint256
withdrawableRewardOf(address) → uint256
withdrawnReward(address) → uint256
Events (6)
ApprovalPermanentMetadataInitializedRewardClaimedRewardConfigurationFinalizedRewardDistributedTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
},
{
"internalType": "string",
"name": "tokenURI_",
"type": "string"
},
{
"internalType": "uint256",
"name": "totalSupply_",
"type": "uint256"
},
{
"internalType": "address",
"name": "supplyReceiver",
"type": "address"
},
{
"internalType": "address",
"name": "rewardConfigurator_",
"type": "address"
},
{
"internalType": "address",
"name": "rewardAsset_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "TransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "TransferFromFailed",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "string",
"name": "tokenURI",
"type": "string"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "metadataDigest",
"type": "bytes32"
}
],
"name": "PermanentMetadataInitialized",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "RewardClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "distributor",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "eligibleSupply",
"type": "uint256"
}
],
"name": "RewardConfigurationFinalized",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "distributor",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "RewardDistributed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "claimReward",
"outputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "distributeReward",
"outputs": [
{
"internalType": "uint256",
"name": "received",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "eligibleSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "distributor",
"type": "address"
},
{
"internalType": "address[]",
"name": "excluded",
"type": "address[]"
}
],
"name": "finalizeRewardConfiguration",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "magnifiedRewardPerShare",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rewardAsset",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rewardConfigurator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rewardDistributor",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "rewardExcluded",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rewardsConfigured",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenURI",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalRewardsDistributed",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "withdrawableRewardOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "withdrawnReward",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {IERC20Minimal} from "../interfaces/IVlady.sol";
import {SafeTransfer, ReentrancyGuard} from "../libraries/SafeTransfer.sol";
/// @title HooxTokenV3
/// @notice Fixed-supply, tax-free HOOX launch token with an optional immutable
/// reward asset. Reward accounting is configured once by the factory and then frozen.
contract HooxTokenV3 is ReentrancyGuard {
uint256 private constant MAGNITUDE = 2 ** 128;
string public name;
string public symbol;
/// @notice ERC-1046 style immutable metadata document URI. Usually ipfs://<CID>.
string public tokenURI;
uint8 public constant decimals = 18;
uint256 public immutable totalSupply;
address public immutable rewardAsset;
address public immutable rewardConfigurator;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
bool public rewardsConfigured;
address public rewardDistributor;
uint256 public eligibleSupply;
uint256 public magnifiedRewardPerShare;
uint256 public totalRewardsDistributed;
mapping(address => bool) public rewardExcluded;
mapping(address => int256) private _rewardCorrection;
mapping(address => uint256) public withdrawnReward;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event PermanentMetadataInitialized(string tokenURI, bytes32 indexed metadataDigest);
event RewardConfigurationFinalized(address indexed distributor, uint256 eligibleSupply);
event RewardDistributed(address indexed distributor, uint256 amount);
event RewardClaimed(address indexed account, uint256 amount);
constructor(
string memory name_,
string memory symbol_,
string memory tokenURI_,
uint256 totalSupply_,
address supplyReceiver,
address rewardConfigurator_,
address rewardAsset_
) {
require(bytes(name_).length != 0 && bytes(name_).length <= 32, "name");
require(bytes(symbol_).length != 0 && bytes(symbol_).length <= 10, "symbol");
bytes memory uri = bytes(tokenURI_);
require(uri.length > 7 && uri.length <= 200, "metadata URI");
require(
uri[0] == 0x69 && uri[1] == 0x70 && uri[2] == 0x66 && uri[3] == 0x73
&& uri[4] == 0x3a && uri[5] == 0x2f && uri[6] == 0x2f,
"metadata scheme"
);
require(totalSupply_ != 0 && supplyReceiver != address(0) && rewardConfigurator_ != address(0), "constructor");
name = name_;
symbol = symbol_;
tokenURI = tokenURI_;
emit PermanentMetadataInitialized(tokenURI_, keccak256(uri));
totalSupply = totalSupply_;
rewardAsset = rewardAsset_;
rewardConfigurator = rewardConfigurator_;
balanceOf[supplyReceiver] = totalSupply_;
emit Transfer(address(0), supplyReceiver, totalSupply_);
}
function transfer(address to, uint256 amount) external returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) {
require(allowed >= amount, "allowance");
unchecked { allowance[from][msg.sender] = allowed - amount; }
emit Approval(from, msg.sender, allowance[from][msg.sender]);
}
_transfer(from, to, amount);
return true;
}
function finalizeRewardConfiguration(address distributor, address[] calldata excluded) external {
require(msg.sender == rewardConfigurator, "configurator only");
require(!rewardsConfigured && rewardAsset != address(0) && distributor != address(0), "reward config");
rewardsConfigured = true;
rewardDistributor = distributor;
uint256 excludedBalance;
for (uint256 i; i < excluded.length; ++i) {
address account = excluded[i];
require(account != address(0), "zero excluded");
if (!rewardExcluded[account]) {
rewardExcluded[account] = true;
excludedBalance += balanceOf[account];
}
}
eligibleSupply = totalSupply - excludedBalance;
emit RewardConfigurationFinalized(distributor, eligibleSupply);
}
function distributeReward(uint256 amount) external nonReentrant returns (uint256 received) {
require(rewardsConfigured && msg.sender == rewardDistributor, "distributor only");
require(amount != 0 && eligibleSupply != 0, "no eligible supply");
uint256 beforeBalance = IERC20Minimal(rewardAsset).balanceOf(address(this));
SafeTransfer.safeTransferFrom(rewardAsset, msg.sender, address(this), amount);
received = IERC20Minimal(rewardAsset).balanceOf(address(this)) - beforeBalance;
require(received != 0, "zero reward");
magnifiedRewardPerShare += received * MAGNITUDE / eligibleSupply;
totalRewardsDistributed += received;
emit RewardDistributed(msg.sender, received);
}
function withdrawableRewardOf(address account) public view returns (uint256) {
if (!rewardsConfigured || rewardExcluded[account]) return 0;
uint256 magnified = magnifiedRewardPerShare * balanceOf[account];
require(magnified <= uint256(type(int256).max), "reward overflow");
int256 corrected = int256(magnified) + _rewardCorrection[account];
if (corrected <= 0) return 0;
uint256 accumulated = uint256(corrected) / MAGNITUDE;
uint256 withdrawn = withdrawnReward[account];
return accumulated > withdrawn ? accumulated - withdrawn : 0;
}
function claimReward() external nonReentrant returns (uint256 amount) {
amount = withdrawableRewardOf(msg.sender);
require(amount != 0, "nothing");
withdrawnReward[msg.sender] += amount;
SafeTransfer.safeTransfer(rewardAsset, msg.sender, amount);
emit RewardClaimed(msg.sender, amount);
}
function _transfer(address from, address to, uint256 amount) private {
require(to != address(0), "zero address");
uint256 fromBalance = balanceOf[from];
require(fromBalance >= amount, "balance");
if (rewardsConfigured && amount != 0) _moveRewardWeight(from, to, amount);
unchecked {
balanceOf[from] = fromBalance - amount;
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
}
function _moveRewardWeight(address from, address to, uint256 amount) private {
bool fromExcluded = rewardExcluded[from];
bool toExcluded = rewardExcluded[to];
uint256 correctionRaw = magnifiedRewardPerShare * amount;
require(correctionRaw <= uint256(type(int256).max), "correction overflow");
int256 correction = int256(correctionRaw);
if (!fromExcluded) _rewardCorrection[from] += correction;
if (!toExcluded) _rewardCorrection[to] -= correction;
if (fromExcluded && !toExcluded) eligibleSupply += amount;
else if (!fromExcluded && toExcluded) eligibleSupply -= amount;
}
}
Chain explorer3788msChain node82ms