MemeFolioToken

0xc2217b52901763c6eaba87b20d13e91d18764ea6

Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
3,683 bytes
ABI entries
29
13 read · 3 write
License
none

Contract information

Address
0xc2217b52901763c6eaba87b20d13e91d18764ea6
Chain
Robinhood Chain (4663)
Compiler
v0.8.26+commit.8a97fa7a
Optimization
Enabled
Creation tx
0xb4259e0665…a6fa637c12

Token

Name
Meme ETF
Symbol
mETF
Decimals
18
Holders
182

Read contract (13)

allowance(address, address)uint256
balanceOf(address)uint256
decimals()uint8
eligibleHolderCount()uint256
eligibleSupply()uint256
getPastBalance(address, uint256)uint256
getPastEligibleHolderCount(uint256)uint256
getPastEligibleSupply(uint256)uint256
initialSupply()uint256
minHolderBalance()uint256
name()string
symbol()string
totalSupply()uint256

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "internalType": "string",
        "name": "name_",
        "type": "string"
      },
      {
        "internalType": "string",
        "name": "symbol_",
        "type": "string"
      },
      {
        "internalType": "uint256",
        "name": "supply_",
        "type": "uint256"
      },
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "minHolderBalance_",
        "type": "uint256"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "CheckpointUnorderedInsertion",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "allowance",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "needed",
        "type": "uint256"
      }
    ],
    "name": "ERC20InsufficientAllowance",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "balance",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "needed",
        "type": "uint256"
      }
    ],
    "name": "ERC20InsufficientBalance",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "approver",
        "type": "address"
      }
    ],
    "name": "ERC20InvalidApprover",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "receiver",
        "type": "address"
      }
    ],
    "name": "ERC20InvalidReceiver",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "sender",
        "type": "address"
      }
    ],
    "name": "ERC20InvalidSender",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }
    ],
    "name": "ERC20InvalidSpender",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "blockNumber",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "currentBlock",
        "type": "uint256"
      }
    ],
    "name": "FutureLookup",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRecipient",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSupply",
    "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": 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": "owner",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      }
    ],
    "name": "allowance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "spender",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "approve",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      }
    ],
    "name": "balanceOf",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "eligibleHolderCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "eligibleSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "blockNumber",
        "type": "uint256"
      }
    ],
    "name": "getPastBalance",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "blockNumber",
        "type": "uint256"
      }
    ],
    "name": "getPastEligibleHolderCount",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "blockNumber",
        "type": "uint256"
      }
    ],
    "name": "getPastEligibleSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "initialSupply",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "minHolderBalance",
    "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": "symbol",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "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": "value",
        "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": "value",
        "type": "uint256"
      }
    ],
    "name": "transferFrom",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "function"
  }
]

Source code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Checkpoints } from "@openzeppelin/contracts/utils/structs/Checkpoints.sol";

/// @title MemeFolioToken
/// @notice Fixed-supply launch token with immutable historical balance checkpoints.
/// @dev Balance, threshold-eligible supply and eligible-holder-count checkpoints let the distributor
///      verify an offchain-enumerated holder batch without trusting uploaded balances.
contract MemeFolioToken is ERC20 {
    using Checkpoints for Checkpoints.Trace256;

    uint256 public immutable initialSupply;
    uint256 public immutable minHolderBalance;
    uint256 public eligibleSupply;
    uint256 public eligibleHolderCount;

    mapping(address holder => Checkpoints.Trace256 trace) private _balanceCheckpoints;
    Checkpoints.Trace256 private _eligibleSupplyCheckpoints;
    Checkpoints.Trace256 private _eligibleHolderCountCheckpoints;

    error InvalidRecipient();
    error InvalidSupply();
    error FutureLookup(uint256 blockNumber, uint256 currentBlock);

    constructor(
        string memory name_,
        string memory symbol_,
        uint256 supply_,
        address recipient,
        uint256 minHolderBalance_
    ) ERC20(name_, symbol_) {
        if (recipient == address(0)) revert InvalidRecipient();
        if (supply_ == 0 || minHolderBalance_ == 0 || minHolderBalance_ > supply_) {
            revert InvalidSupply();
        }

        initialSupply = supply_;
        minHolderBalance = minHolderBalance_;
        _mint(recipient, supply_);
    }

    function getPastBalance(address account, uint256 blockNumber) external view returns (uint256) {
        _validatePastBlock(blockNumber);
        return _balanceCheckpoints[account].upperLookupRecent(blockNumber);
    }

    function getPastEligibleSupply(uint256 blockNumber) external view returns (uint256) {
        _validatePastBlock(blockNumber);
        return _eligibleSupplyCheckpoints.upperLookupRecent(blockNumber);
    }

    function getPastEligibleHolderCount(uint256 blockNumber) external view returns (uint256) {
        _validatePastBlock(blockNumber);
        return _eligibleHolderCountCheckpoints.upperLookupRecent(blockNumber);
    }

    function _update(address from, address to, uint256 value) internal override {
        uint256 fromBefore = from == address(0) ? 0 : balanceOf(from);
        uint256 toBefore = to == address(0) ? 0 : balanceOf(to);

        super._update(from, to, value);

        uint256 updatedEligibleSupply = eligibleSupply;
        uint256 updatedEligibleHolderCount = eligibleHolderCount;
        if (from != address(0)) {
            uint256 fromAfter = balanceOf(from);
            updatedEligibleSupply = updatedEligibleSupply - _eligibleContribution(fromBefore)
                + _eligibleContribution(fromAfter);
            updatedEligibleHolderCount = updatedEligibleHolderCount
                - _eligibleAccountContribution(fromBefore) + _eligibleAccountContribution(fromAfter);
            _writeAccountCheckpoint(from, fromAfter);
        }
        if (to != address(0) && to != from) {
            uint256 toAfter = balanceOf(to);
            updatedEligibleSupply = updatedEligibleSupply - _eligibleContribution(toBefore)
                + _eligibleContribution(toAfter);
            updatedEligibleHolderCount = updatedEligibleHolderCount
                - _eligibleAccountContribution(toBefore) + _eligibleAccountContribution(toAfter);
            _writeAccountCheckpoint(to, toAfter);
        }

        if (updatedEligibleSupply != eligibleSupply) {
            eligibleSupply = updatedEligibleSupply;
            _eligibleSupplyCheckpoints.push(block.number, updatedEligibleSupply);
        }
        if (updatedEligibleHolderCount != eligibleHolderCount) {
            eligibleHolderCount = updatedEligibleHolderCount;
            _eligibleHolderCountCheckpoints.push(block.number, updatedEligibleHolderCount);
        }
    }

    function _writeAccountCheckpoint(address account, uint256 balance) private {
        _balanceCheckpoints[account].push(block.number, balance);
    }

    function _eligibleContribution(uint256 balance) private view returns (uint256) {
        return balance >= minHolderBalance ? balance : 0;
    }

    function _eligibleAccountContribution(uint256 balance) private view returns (uint256) {
        return balance >= minHolderBalance ? 1 : 0;
    }

    function _validatePastBlock(uint256 blockNumber) private view {
        if (blockNumber >= block.number) revert FutureLookup(blockNumber, block.number);
    }
}
Chain explorer4562msChain node82ms