EquifoldRestrictedTokenV4

0x82c6fe1082dcc6e71e9411cef2846a0a107c34ab

Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
4,909 bytes
ABI entries
49
23 read · 6 write
License
none

Contract information

Address
0x82c6fe1082dcc6e71e9411cef2846a0a107c34ab
Chain
Robinhood Chain (4663)
Compiler
v0.8.24+commit.e11b9ed9
Optimization
Enabled
Creation tx
0xd1cfd1b539…39ce34a603

Token

Name
MemeSoft
Symbol
MSFT
Decimals
18
Holders
72

Read contract (23)

MAX_DECIMALS()uint8
MIN_DECIMALS()uint8
TOKEN_VERSION()uint32
allowance(address, address)uint256
balanceOf(address)uint256
creator()address
decimals()uint8
factory()address
isBurnable()bool
launchBlock()uint256
launchContract()address
launchTimestamp()uint48
maxBuysLimit()uint256
maxTxBps()uint16
maxWalletBps()uint16
maxWalletLimit()uint256
metadataURI()string
name()string
poolManager()address
projectNumber()uint256
restrictionEndBlock()uint256
symbol()string
totalSupply()uint256

Events (2)

ApprovalTransfer

ABI

[
  {
    "inputs": [
      {
        "components": [
          {
            "internalType": "string",
            "name": "name",
            "type": "string"
          },
          {
            "internalType": "string",
            "name": "symbol",
            "type": "string"
          },
          {
            "internalType": "uint8",
            "name": "decimals",
            "type": "uint8"
          },
          {
            "internalType": "uint256",
            "name": "initialSupply",
            "type": "uint256"
          },
          {
            "internalType": "bool",
            "name": "burnable",
            "type": "bool"
          },
          {
            "internalType": "address",
            "name": "creator",
            "type": "address"
          },
          {
            "internalType": "uint256",
            "name": "projectNumber",
            "type": "uint256"
          },
          {
            "internalType": "string",
            "name": "metadataURI",
            "type": "string"
          },
          {
            "internalType": "address",
            "name": "launchContract",
            "type": "address"
          }
        ],
        "internalType": "struct EquifoldToken.Initialization",
        "name": "initial",
        "type": "tuple"
      },
      {
        "components": [
          {
            "internalType": "address",
            "name": "poolManager",
            "type": "address"
          },
          {
            "internalType": "uint16",
            "name": "maxWalletBps",
            "type": "uint16"
          },
          {
            "internalType": "uint16",
            "name": "maxTxBps",
            "type": "uint16"
          },
          {
            "internalType": "uint32",
            "name": "restrictionBlocks",
            "type": "uint32"
          }
        ],
        "internalType": "struct EquifoldRestrictedTokenV4.RestrictionConfigV4",
        "name": "restriction",
        "type": "tuple"
      }
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {
    "inputs": [],
    "name": "BurningDisabled",
    "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": [],
    "name": "InvalidAddress",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidDecimals",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidMetadata",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidProject",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidRestrictionConfig",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "InvalidSupply",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "LaunchBlockBuyBlocked",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "cumulative",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "limit",
        "type": "uint256"
      }
    ],
    "name": "MaxBuysExceeded",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "resultingBalance",
        "type": "uint256"
      },
      {
        "internalType": "uint256",
        "name": "limit",
        "type": "uint256"
      }
    ],
    "name": "MaxWalletExceeded",
    "type": "error"
  },
  {
    "inputs": [],
    "name": "NotLaunchContract",
    "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": [],
    "name": "MAX_DECIMALS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "MIN_DECIMALS",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "TOKEN_VERSION",
    "outputs": [
      {
        "internalType": "uint32",
        "name": "",
        "type": "uint32"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "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": [
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "burn",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "account",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "value",
        "type": "uint256"
      }
    ],
    "name": "burnFrom",
    "outputs": [],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "creator",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "decimals",
    "outputs": [
      {
        "internalType": "uint8",
        "name": "",
        "type": "uint8"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "factory",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "isBurnable",
    "outputs": [
      {
        "internalType": "bool",
        "name": "",
        "type": "bool"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchBlock",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchContract",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "launchTimestamp",
    "outputs": [
      {
        "internalType": "uint48",
        "name": "",
        "type": "uint48"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxBuysLimit",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxTxBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWalletBps",
    "outputs": [
      {
        "internalType": "uint16",
        "name": "",
        "type": "uint16"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "maxWalletLimit",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "metadataURI",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "name",
    "outputs": [
      {
        "internalType": "string",
        "name": "",
        "type": "string"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "poolManager",
    "outputs": [
      {
        "internalType": "address",
        "name": "",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "projectNumber",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [],
    "name": "restrictionEndBlock",
    "outputs": [
      {
        "internalType": "uint256",
        "name": "",
        "type": "uint256"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "recipient",
        "type": "address"
      }
    ],
    "name": "setInitialBuyRecipient",
    "outputs": [],
    "stateMutability": "nonpayable",
    "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.24;

import { EquifoldToken } from "./EquifoldToken.sol";

/// @title Equifold restricted launch token (stock-paired v4 v2)
/// @notice EquifoldToken whose v4 pool BUYS are limited during a fixed launch window. In
///         Uniswap v4 every pool's balances are custodied by the singleton PoolManager, so a
///         "pool buy" is any transfer with `from == poolManager` — one check covers every v4
///         pool of this token, with no per-fee-tier probing. Launch-block buys are blocked
///         (except the module's atomic creator dev buy), then each recipient is capped at
///         {maxWalletBps} balance and {maxTxBps} cumulative buys until {restrictionEndBlock}.
///         Sells and wallet-to-wallet transfers are never restricted; after the window the
///         token behaves as a plain EquifoldToken. External routers can therefore trade the
///         pool from block launchBlock+1 — only cap-breaking buys revert.
contract EquifoldRestrictedTokenV4 is EquifoldToken {
    error InvalidRestrictionConfig();
    error LaunchBlockBuyBlocked(address recipient);
    error MaxBuysExceeded(address recipient, uint256 cumulative, uint256 limit);
    error MaxWalletExceeded(address recipient, uint256 resultingBalance, uint256 limit);
    error NotLaunchContract();

    struct RestrictionConfigV4 {
        address poolManager;
        uint16 maxWalletBps;
        uint16 maxTxBps;
        uint32 restrictionBlocks;
    }

    uint16 private constant BPS_DENOMINATOR = 10_000;

    address public immutable poolManager;
    uint16 public immutable maxWalletBps;
    uint16 public immutable maxTxBps;
    uint256 public immutable launchBlock;
    uint256 public immutable restrictionEndBlock;

    address private _initialBuyRecipient;
    mapping(address recipient => uint256 bought) private _restrictedPoolBuys;

    constructor(
        Initialization memory initial,
        RestrictionConfigV4 memory restriction
    ) EquifoldToken(initial) {
        if (
            restriction.poolManager == address(0) || restriction.poolManager.code.length == 0
                || restriction.maxWalletBps == 0 || restriction.maxWalletBps > BPS_DENOMINATOR
                || restriction.maxTxBps < restriction.maxWalletBps
                || restriction.maxTxBps > BPS_DENOMINATOR || restriction.restrictionBlocks == 0
        ) revert InvalidRestrictionConfig();
        poolManager = restriction.poolManager;
        maxWalletBps = restriction.maxWalletBps;
        maxTxBps = restriction.maxTxBps;
        launchBlock = block.number;
        restrictionEndBlock = block.number + restriction.restrictionBlocks;
    }

    function maxWalletLimit() public view returns (uint256) {
        return (totalSupply() * maxWalletBps) / BPS_DENOMINATOR;
    }

    function maxBuysLimit() public view returns (uint256) {
        return (totalSupply() * maxTxBps) / BPS_DENOMINATOR;
    }

    function setInitialBuyRecipient(
        address recipient
    ) external {
        if (msg.sender != launchContract) revert NotLaunchContract();
        _initialBuyRecipient = recipient;
    }

    function _update(
        address from,
        address to,
        uint256 value
    ) internal override {
        if (
            from != address(0) && to != address(0) && block.number <= restrictionEndBlock
                && from == poolManager
        ) {
            bool exemptBuy = block.number == launchBlock && _initialBuyRecipient != address(0)
                && to == _initialBuyRecipient;
            if (!exemptBuy) {
                if (block.number == launchBlock) revert LaunchBlockBuyBlocked(to);
                uint256 resultingBalance = balanceOf(to) + value;
                uint256 walletLimit = maxWalletLimit();
                if (resultingBalance > walletLimit) {
                    revert MaxWalletExceeded(to, resultingBalance, walletLimit);
                }
                uint256 cumulative = _restrictedPoolBuys[to] + value;
                uint256 buysLimit = maxBuysLimit();
                if (cumulative > buysLimit) {
                    revert MaxBuysExceeded(to, cumulative, buysLimit);
                }
                _restrictedPoolBuys[to] = cumulative;
            }
        }
        super._update(from, to, value);
    }
}
Chain explorer4128msChain node89ms