ZxoarLauncher
0x2c42840bd90224bf2850b09a9a00dd3a9f9cc66f
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
13,736 bytes
ABI entries
37
19 read · 5 write
License
none
Contract information
- Address
- 0x2c42840bd90224bf2850b09a9a00dd3a9f9cc66f
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xa4B8b0f811…010A0602C9
- Creation tx
- 0x56ba47ec6f…b356dcde20
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (19)
CURVE_SUPPLY() → uint256
GRAD_RESERVE() → uint256
GRAD_TICK_LOWER() → int24
MAX_METADATA_URI_BYTES() → uint256
TICK_LOWER() → int24
TICK_SPACING() → int24
TICK_UPPER() → int24
TOTAL_SUPPLY() → uint256
allTokens(uint256) → address
buyTickLimit(address) → int24
graduated(address) → bool
hook() → address
launches(address) → address, address, string, uint64, uint128
poolKeyFor(address) → tuple
poolManager() → address
protocolFeeRecipient() → address
router() → address
tokenCount() → uint256
weth() → address
Events (3)
FeesCollectedGraduatedTokenLaunched
ABI
[
{
"inputs": [
{
"internalType": "contract IPoolManager",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "contract IHooks",
"name": "hook_",
"type": "address"
},
{
"internalType": "address",
"name": "protocolFeeRecipient_",
"type": "address"
},
{
"internalType": "contract IWETH",
"name": "weth_",
"type": "address"
},
{
"internalType": "address",
"name": "router_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyGraduated",
"type": "error"
},
{
"inputs": [],
"name": "CurveNotFilled",
"type": "error"
},
{
"inputs": [],
"name": "MetadataUriTooLong",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "NotRouter",
"type": "error"
},
{
"inputs": [],
"name": "UnexpectedEthOwed",
"type": "error"
},
{
"inputs": [],
"name": "UnknownToken",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "creatorWethFallback",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "protocolWethFallback",
"type": "bool"
}
],
"name": "FeesCollected",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint128",
"name": "gradLiquidity",
"type": "uint128"
}
],
"name": "Graduated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"indexed": true,
"internalType": "PoolId",
"name": "poolId",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"indexed": false,
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
}
],
"name": "TokenLaunched",
"type": "event"
},
{
"inputs": [],
"name": "CURVE_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "GRAD_RESERVE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "GRAD_TICK_LOWER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_METADATA_URI_BYTES",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_LOWER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_SPACING",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TICK_UPPER",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TOTAL_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "allTokens",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "buyTickLimit",
"outputs": [
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "collectFees",
"outputs": [
{
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "graduate",
"outputs": [
{
"internalType": "uint128",
"name": "gradLiquidity",
"type": "uint128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "graduated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "hook",
"outputs": [
{
"internalType": "contract IHooks",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
}
],
"name": "launch",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
}
],
"name": "launchFor",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "launches",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "creator",
"type": "address"
},
{
"internalType": "string",
"name": "metadataURI",
"type": "string"
},
{
"internalType": "uint64",
"name": "createdAt",
"type": "uint64"
},
{
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "poolKeyFor",
"outputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "protocolFeeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "router",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokenCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "unlockCallback",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "weth",
"outputs": [
{
"internalType": "contract IWETH",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {IUnlockCallback} from "v4-core/src/interfaces/callback/IUnlockCallback.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {PoolId, PoolIdLibrary} from "v4-core/src/types/PoolId.sol";
import {Currency} from "v4-core/src/types/Currency.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";
import {FullMath} from "v4-core/src/libraries/FullMath.sol";
import {FixedPoint96} from "v4-core/src/libraries/FixedPoint96.sol";
import {LPFeeLibrary} from "v4-core/src/libraries/LPFeeLibrary.sol";
import {StateLibrary} from "v4-core/src/libraries/StateLibrary.sol";
/// @dev Fixed-supply token; the entire supply is minted to the launcher and
/// immediately deposited into the locked pool position. No owner, no
/// mint, no hooks — nothing a creator can rug with.
contract ZxoarToken is ERC20 {
constructor(string memory n, string memory s, uint256 supply, address to) ERC20(n, s) {
_mint(to, supply);
}
}
/// @title ZxoarLauncher
/// @notice Agent-first fair-launch factory for Uniswap V4.
///
/// launch() in one transaction:
/// 1. deploys a fixed-supply token,
/// 2. initializes a native-ETH/token V4 pool wired to SnipeGuardHook
/// (dynamic fee: 50% -> 1% over 3 minutes),
/// 3. deposits 95% of supply as a single-sided position spanning
/// [TICK_LOWER, TICK_UPPER] — the pool's own math is the bonding
/// curve — and escrows the remaining 5% as the graduation
/// reserve, deployed by permissionless graduate() as locked
/// open-market liquidity the moment the curve fills. There is
/// no migration, ever: 100% of supply is locked pool liquidity.
///
/// The position is owned by this contract and there is deliberately
/// no code path that removes liquidity: it is locked forever. Swap
/// fees accrued to the position are claimable via collectFees(),
/// split 50/50 between the token's creator and the protocol.
contract ZxoarLauncher is IUnlockCallback, ReentrancyGuard {
using SafeERC20 for IERC20;
using PoolIdLibrary for PoolKey;
using StateLibrary for IPoolManager;
// ---------------------------------------------------------------- config
uint256 public constant TOTAL_SUPPLY = 1_000_000_000e18;
int24 public constant TICK_SPACING = 200;
/// @notice Max bytes for a token's metadataURI. Enforced on-chain so the
/// 32KB cap the off-chain builder advertises is actually guaranteed,
/// and to bound the SSTORE cost / event size of a launch.
uint256 public constant MAX_METADATA_URI_BYTES = 32_768;
/// Price band, expressed in currency1-per-currency0 ticks (token per ETH).
/// TICK_UPPER is the *launch* price (~1.4e-9 ETH per token, ~1.4 Ξ market
/// cap); TICK_LOWER is the top of the ladder (~2.05e-8 ETH per token,
/// ~20.5 Ξ market cap). ETH buys push the tick downward through the band.
int24 public constant TICK_LOWER = 177_000;
int24 public constant TICK_UPPER = 203_800;
/// Graduation: 95% of supply forms the curve; 5% is escrowed here and
/// deployed by permissionless graduate() as locked liquidity spanning
/// [GRAD_TICK_LOWER, TICK_LOWER) — the same band width again (~14.6x more
/// price runway, curve top ~20.6 ETH FDV -> graduated top ~300 ETH FDV).
uint256 public constant CURVE_SUPPLY = 950_000_000e18;
uint256 public constant GRAD_RESERVE = 50_000_000e18;
int24 public constant GRAD_TICK_LOWER = 150_200;
IPoolManager public immutable poolManager;
IHooks public immutable hook;
address public immutable protocolFeeRecipient;
IWETH public immutable weth;
address public immutable router;
// -------------------------------------------------------------- registry
struct Launch {
address token;
address creator;
string metadataURI;
uint64 createdAt;
uint128 liquidity;
}
mapping(address => Launch) public launches; // token => Launch
address[] public allTokens;
mapping(address => bool) public graduated; // token => curve filled & seed deployed
event TokenLaunched(
address indexed token,
address indexed creator,
PoolId indexed poolId,
string name,
string symbol,
string metadataURI,
uint128 liquidity
);
event FeesCollected(
address indexed token,
address indexed creator,
uint256 ethAmount,
uint256 tokenAmount,
bool creatorWethFallback,
bool protocolWethFallback
);
event Graduated(address indexed token, uint128 gradLiquidity);
error UnknownToken();
error NotPoolManager();
error NotRouter();
error UnexpectedEthOwed();
error ZeroAddress();
error MetadataUriTooLong();
error AlreadyGraduated();
error CurveNotFilled();
uint8 private constant ACTION_ADD = 1;
uint8 private constant ACTION_COLLECT = 2;
uint8 private constant ACTION_GRADUATE = 3;
constructor(IPoolManager poolManager_, IHooks hook_, address protocolFeeRecipient_, IWETH weth_, address router_) {
if (
address(poolManager_) == address(0) || address(hook_) == address(0)
|| protocolFeeRecipient_ == address(0) || address(weth_) == address(0) || router_ == address(0)
) revert ZeroAddress();
poolManager = poolManager_;
hook = hook_;
protocolFeeRecipient = protocolFeeRecipient_;
weth = weth_;
router = router_;
}
// -------------------------------------------------------------- launching
/// @notice Launch a new token. Caller becomes the creator.
function launch(string calldata name, string calldata symbol, string calldata metadataURI)
external
returns (address token)
{
return _launch(msg.sender, name, symbol, metadataURI);
}
/// @notice Launch a new token on behalf of a creator. Router-only.
/// @dev Used by launchAndBuy to preserve creator attribution when the router
/// is msg.sender. Without this, all fee revenue would route to the router.
function launchFor(address creator, string calldata name, string calldata symbol, string calldata metadataURI)
external
returns (address token)
{
if (msg.sender != router) revert NotRouter();
return _launch(creator, name, symbol, metadataURI);
}
function _launch(address creator, string calldata name, string calldata symbol, string calldata metadataURI)
internal
returns (address token)
{
if (bytes(metadataURI).length > MAX_METADATA_URI_BYTES) revert MetadataUriTooLong();
token = address(new ZxoarToken(name, symbol, TOTAL_SUPPLY, address(this)));
PoolKey memory key = poolKeyFor(token);
// Initialize exactly at the top tick of the band: the position is then
// 100% token (currency1); the first ETH buy moves price into the band.
uint160 sqrtStart = TickMath.getSqrtPriceAtTick(TICK_UPPER);
poolManager.initialize(key, sqrtStart);
// Liquidity such that the position holds the curve supply (95%); the
// graduation reserve stays escrowed here until graduate().
// amount1 = L * (sqrtU - sqrtL) / Q96 => L = supply * Q96 / (sqrtU - sqrtL)
uint160 sqrtL = TickMath.getSqrtPriceAtTick(TICK_LOWER);
uint160 sqrtU = sqrtStart;
uint128 liquidity =
uint128(FullMath.mulDiv(CURVE_SUPPLY, FixedPoint96.Q96, uint256(sqrtU - sqrtL)));
poolManager.unlock(abi.encode(ACTION_ADD, token, liquidity));
launches[token] = Launch({
token: token,
creator: creator,
metadataURI: metadataURI,
createdAt: uint64(block.timestamp),
liquidity: liquidity
});
allTokens.push(token);
emit TokenLaunched(token, creator, key.toId(), name, symbol, metadataURI, liquidity);
}
// ----------------------------------------------------------- fee stream
/// @notice Claim swap fees accrued to the locked position for `token`.
/// Anyone can poke this; proceeds always split 50/50 between the
/// token's creator and the protocol. Principal cannot be touched.
function collectFees(address token) external nonReentrant returns (uint256 ethAmount, uint256 tokenAmount) {
Launch memory l = launches[token];
if (l.token == address(0)) revert UnknownToken();
bytes memory result = poolManager.unlock(abi.encode(ACTION_COLLECT, token, uint128(0)));
(ethAmount, tokenAmount) = abi.decode(result, (uint256, uint256));
uint256 ethCreator = ethAmount / 2;
uint256 tokCreator = tokenAmount / 2;
bool creatorFallback;
bool protocolFallback;
if (ethCreator > 0) creatorFallback = _sendEth(l.creator, ethCreator);
if (ethAmount - ethCreator > 0) protocolFallback = _sendEth(protocolFeeRecipient, ethAmount - ethCreator);
if (tokCreator > 0) IERC20(token).safeTransfer(l.creator, tokCreator);
if (tokenAmount - tokCreator > 0) {
IERC20(token).safeTransfer(protocolFeeRecipient, tokenAmount - tokCreator);
}
emit FeesCollected(token, l.creator, ethAmount, tokenAmount, creatorFallback, protocolFallback);
}
// ------------------------------------------------------------ graduation
/// @notice Deploy the graduation reserve as locked open-market liquidity in
/// [GRAD_TICK_LOWER, TICK_LOWER). Permissionless — callable by
/// anyone once the curve is bought through its floor. One-way and
/// once per token; the seed is locked exactly like the curve.
function graduate(address token) external nonReentrant returns (uint128 gradLiquidity) {
Launch memory l = launches[token];
if (l.token == address(0)) revert UnknownToken();
if (graduated[token]) revert AlreadyGraduated();
(, int24 tick,,) = poolManager.getSlot0(poolKeyFor(token).toId());
if (tick > TICK_LOWER) revert CurveNotFilled();
graduated[token] = true;
uint160 sqrtGL = TickMath.getSqrtPriceAtTick(GRAD_TICK_LOWER);
uint160 sqrtGU = TickMath.getSqrtPriceAtTick(TICK_LOWER);
gradLiquidity = uint128(FullMath.mulDiv(GRAD_RESERVE, FixedPoint96.Q96, uint256(sqrtGU - sqrtGL)));
poolManager.unlock(abi.encode(ACTION_GRADUATE, token, gradLiquidity));
emit Graduated(token, gradLiquidity);
}
/// @notice Lowest tick routers may let buys reach: the curve floor before
/// graduation, the graduated-band floor after. Single source of
/// truth so no caller can walk price into empty tick space.
function buyTickLimit(address token) public view returns (int24) {
return graduated[token] ? GRAD_TICK_LOWER : TICK_LOWER;
}
// --------------------------------------------------------- v4 callback
function unlockCallback(bytes calldata data) external returns (bytes memory) {
if (msg.sender != address(poolManager)) revert NotPoolManager();
(uint8 action, address token, uint128 liquidity) = abi.decode(data, (uint8, address, uint128));
PoolKey memory key = poolKeyFor(token);
if (action == ACTION_ADD || action == ACTION_GRADUATE) {
bool grad = action == ACTION_GRADUATE;
(BalanceDelta delta,) = poolManager.modifyLiquidity(
key,
IPoolManager.ModifyLiquidityParams({
tickLower: grad ? GRAD_TICK_LOWER : TICK_LOWER,
tickUpper: grad ? TICK_LOWER : TICK_UPPER,
liquidityDelta: int256(uint256(liquidity)),
salt: bytes32(0)
}),
""
);
// Both mints owe only token (currency1): launch initializes at the
// band top, and graduation mints at/above its range top. Any ETH
// owed means price sits mid-band — revert; graduate() is retryable.
if (delta.amount0() != 0) revert UnexpectedEthOwed();
uint256 owed = uint256(uint128(-delta.amount1()));
poolManager.sync(key.currency1);
IERC20(token).safeTransfer(address(poolManager), owed);
poolManager.settle();
return "";
}
// ACTION_COLLECT: modifyLiquidity with zero delta realizes accrued fees.
(BalanceDelta feeDelta,) = poolManager.modifyLiquidity(
key,
IPoolManager.ModifyLiquidityParams({
tickLower: TICK_LOWER,
tickUpper: TICK_UPPER,
liquidityDelta: 0,
salt: bytes32(0)
}),
""
);
uint256 ethAmt = feeDelta.amount0() > 0 ? uint256(uint128(feeDelta.amount0())) : 0;
uint256 tokAmt = feeDelta.amount1() > 0 ? uint256(uint128(feeDelta.amount1())) : 0;
if (graduated[token]) {
(BalanceDelta g,) = poolManager.modifyLiquidity(
key,
IPoolManager.ModifyLiquidityParams({
tickLower: GRAD_TICK_LOWER,
tickUpper: TICK_LOWER,
liquidityDelta: 0,
salt: bytes32(0)
}),
""
);
if (g.amount0() > 0) ethAmt += uint256(uint128(g.amount0()));
if (g.amount1() > 0) tokAmt += uint256(uint128(g.amount1()));
}
if (ethAmt > 0) poolManager.take(key.currency0, address(this), ethAmt);
if (tokAmt > 0) poolManager.take(key.currency1, address(this), tokAmt);
return abi.encode(ethAmt, tokAmt);
}
// ---------------------------------------------------------------- views
function poolKeyFor(address token) public view returns (PoolKey memory) {
return PoolKey({
currency0: Currency.wrap(address(0)), // native ETH
currency1: Currency.wrap(token),
fee: LPFeeLibrary.DYNAMIC_FEE_FLAG,
tickSpacing: TICK_SPACING,
hooks: hook
});
}
function tokenCount() external view returns (uint256) {
return allTokens.length;
}
/// @dev Send ETH to `to`. If raw send fails (e.g. recipient is a contract
/// without payable receive), wrap to WETH and transfer instead.
/// WETH transfer cannot be recipient-reverted, so this never fails
/// for reasons outside our control.
/// @return usedWethFallback True if raw send failed and WETH was used.
function _sendEth(address to, uint256 amount) internal returns (bool usedWethFallback) {
(bool ok,) = to.call{value: amount}("");
if (!ok) {
// Wrap to WETH and transfer — cannot be reverted by recipient
weth.deposit{value: amount}();
require(weth.transfer(to, amount), "weth transfer failed");
return true;
}
return false;
}
receive() external payable {}
}
Chain explorer1884msChain node77ms