EquifoldRestrictedToken
0xe30144f6a87208275b809a8bc04c70523fbf45f1
Verification
Verified
v0.8.24+commit.e11b9ed9
Type
Contract
5,959 bytes
ABI entries
51
25 read · 6 write
License
none
Contract information
- Address
- 0xe30144f6a87208275b809a8bc04c70523fbf45f1
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.24+commit.e11b9ed9
- Optimization
- Enabled
- Creator
- 0xFe292f3414…E8fa5eEDe1
- Creation tx
- 0x0194bc7aa6…6b32ca2205
Token
- Name
- Kaiten
- Symbol
- KAITEN
- Decimals
- 18
- Holders
- 16
- Market
- View token page →
Read contract (25)
MAX_DECIMALS() → uint8
MIN_DECIMALS() → uint8
TOKEN_VERSION() → uint32
allowance(address, address) → uint256
balanceOf(address) → uint256
creator() → address
decimals() → uint8
dexFactory() → address
factory() → address
isBurnable() → bool
launchBlock() → uint256
launchContract() → address
launchTimestamp() → uint48
maxBuysLimit() → uint256
maxTxBps() → uint16
maxWalletBps() → uint16
maxWalletLimit() → uint256
metadataURI() → string
name() → string
pairToken() → address
poolFee() → uint24
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": "dexFactory",
"type": "address"
},
{
"internalType": "address",
"name": "pairToken",
"type": "address"
},
{
"internalType": "uint24",
"name": "poolFee",
"type": "uint24"
},
{
"internalType": "uint16",
"name": "maxWalletBps",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "maxTxBps",
"type": "uint16"
},
{
"internalType": "uint32",
"name": "restrictionBlocks",
"type": "uint32"
}
],
"internalType": "struct EquifoldRestrictedToken.RestrictionConfig",
"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": "dexFactory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"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": "pairToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolFee",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"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";
import {
IUniswapV3FactoryMinimal,
IUniswapV3PoolMinimal
} from "../interfaces/IUniswapV3Minimal.sol";
/// @title Equifold restricted launch token (gen3 V3-native)
/// @author Equifold
/// @notice EquifoldToken whose pool BUYS are limited during a fixed launch window: launch-block
/// buys are blocked entirely (except the factory's atomic creator dev buy), then each
/// recipient is capped at {maxWalletBps} balance and {maxTxBps} cumulative pool buys until
/// {restrictionEndBlock}. Sells and wallet-to-wallet transfers are never restricted, and
/// after the window the token behaves as a plain EquifoldToken.
contract EquifoldRestrictedToken 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 RestrictionConfig {
address dexFactory;
address pairToken;
uint24 poolFee;
uint16 maxWalletBps;
uint16 maxTxBps;
uint32 restrictionBlocks;
}
uint16 private constant BPS_DENOMINATOR = 10_000;
address public immutable dexFactory;
address public immutable pairToken;
uint24 public immutable poolFee;
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,
RestrictionConfig memory restriction
) EquifoldToken(initial) {
if (
restriction.dexFactory == address(0) || restriction.pairToken == address(0)
|| restriction.dexFactory.code.length == 0 || restriction.poolFee == 0
|| restriction.maxWalletBps == 0 || restriction.maxWalletBps > BPS_DENOMINATOR
|| restriction.maxTxBps < restriction.maxWalletBps
|| restriction.maxTxBps > BPS_DENOMINATOR || restriction.restrictionBlocks == 0
) revert InvalidRestrictionConfig();
dexFactory = restriction.dexFactory;
pairToken = restriction.pairToken;
poolFee = restriction.poolFee;
maxWalletBps = restriction.maxWalletBps;
maxTxBps = restriction.maxTxBps;
launchBlock = block.number;
restrictionEndBlock = block.number + restriction.restrictionBlocks;
}
/// @notice Maximum recipient balance during the restricted launch window.
function maxWalletLimit() public view returns (uint256) {
return (totalSupply() * maxWalletBps) / BPS_DENOMINATOR;
}
/// @notice Cumulative pool-buy limit per recipient during the restricted window.
function maxBuysLimit() public view returns (uint256) {
return (totalSupply() * maxTxBps) / BPS_DENOMINATOR;
}
/// @notice Opens or closes the single-recipient exemption used by the launch module's atomic
/// creator dev buy. Only callable by the launch module bound at deploy. The exemption
/// only bypasses the caps while `block.number == launchBlock`; if left set afterward
/// it has no further effect (see {_update}).
function setInitialBuyRecipient(
address recipient
) external {
if (msg.sender != launchContract) revert NotLaunchContract();
_initialBuyRecipient = recipient;
}
/// @dev Restricts only pool→user transfers (buys) during the launch window. The
/// {_initialBuyRecipient} exemption is scoped to the launch block itself, so it can only
/// ever cover the launch module's atomic creator dev buy — never the 3%/3.3% caps for
/// the rest of the restriction window, even if left set by mistake.
function _update(
address from,
address to,
uint256 value
) internal override {
if (
from != address(0) && to != address(0) && block.number <= restrictionEndBlock
&& _isPairPool(from)
) {
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);
}
/// @dev A "pair pool" is the canonical (token, pairToken, poolFee) V3 pool or any other
/// fee-tier pool of the same pair registered on the same factory, so buys cannot dodge
/// the window through an alternate tier.
function _isPairPool(
address candidate
) private view returns (bool) {
if (candidate.code.length == 0) return false;
address canonicalPool =
IUniswapV3FactoryMinimal(dexFactory).getPool(address(this), pairToken, poolFee);
if (candidate == canonicalPool && canonicalPool != address(0)) return true;
(bool ok, bytes memory data) =
candidate.staticcall(abi.encodeCall(IUniswapV3PoolMinimal.fee, ()));
if (!ok || data.length < 32) return false;
uint24 candidateFee = abi.decode(data, (uint24));
return IUniswapV3FactoryMinimal(dexFactory).getPool(address(this), pairToken, candidateFee)
== candidate;
}
}
Chain explorer2507msChain node79ms