Limonata

SqueezeFactory

ContractVerified
0x39915b0B24Fe5d234FF6D8C65926c06c9962a8e4
Balance
16.658721 LIMO
Verified contractVerified
Contract name
SqueezeFactory
Verified
Fully verified · 2026-07-03 16:23:07 UTC
Compiler
0.8.24+commit.e11b9ed9.Emscripten.clang
Optimization
Yes (200 runs)
EVM version
shanghai
License
-

Source code

// ===== File: src/Squeeze.sol =====
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

// ─────────────────────────────────────────────────────────────────────────────
//  SQUEEZE — a toy bonding-curve launchpad for the Limonata TESTNET (chainId 10777).
//
//  SANDBOX ONLY. Every token created here is play-money: it trades against
//  *testnet* LIMO, which has NO monetary value and NO path to value (no fiat
//  on-ramp, no bridge, no mainnet listing). This exists to demo a pump.fun-style
//  launch + AMM UX on Limonata. It is a game, not an investment.
// ─────────────────────────────────────────────────────────────────────────────

/// @notice Minimal standalone ERC-20. The full supply is minted to the curve
///         (the factory) at birth; users only ever obtain tokens by buying off
///         the curve. The factory is a trusted spender (it can pull a seller's
///         tokens with no approval) so selling is one popup-free transaction.
contract SqueezeToken {
    string public name;
    string public symbol;
    uint8 public constant decimals = 18;
    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    address public immutable curve; // the SqueezeFactory

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    constructor(string memory _name, string memory _symbol, uint256 _supply, address _curve) {
        name = _name;
        symbol = _symbol;
        curve = _curve;
        totalSupply = _supply;
        balanceOf[_curve] = _supply;
        emit Transfer(address(0), _curve, _supply);
    }

    function transfer(address to, uint256 v) external returns (bool) {
        // Donating tokens straight to the curve would corrupt its reserve accounting,
        // so block it; the curve only ever receives tokens via the sell() pull path.
        require(to != curve, "curve");
        _move(msg.sender, to, v);
        return true;
    }

    function approve(address sp, uint256 v) external returns (bool) {
        allowance[msg.sender][sp] = v;
        emit Approval(msg.sender, sp, v);
        return true;
    }

    function transferFrom(address from, address to, uint256 v) external returns (bool) {
        // The curve is trusted: it can only ever pull `from == msg.sender of sell()`,
        // so no approval is needed for the popup-free sell path.
        if (msg.sender != curve) {
            uint256 a = allowance[from][msg.sender];
            if (a != type(uint256).max) {
                require(a >= v, "allowance");
                allowance[from][msg.sender] = a - v;
            }
        }
        _move(from, to, v);
        return true;
    }

    function _move(address from, address to, uint256 v) internal {
        require(to != address(0), "zero");
        require(balanceOf[from] >= v, "balance");
        unchecked {
            balanceOf[from] -= v;
            balanceOf[to] += v;
        }
        emit Transfer(from, to, v);
    }
}

/// @notice The launchpad + AMM. One contract holds every token's native LIMO
///         reserve and runs a constant-product curve with virtual reserves
///         (the pump.fun model): price starts low, rises as supply is bought.
contract SqueezeFactory {
    uint256 public constant SUPPLY = 1_000_000_000 ether; // 1B tokens per launch
    uint256 public constant VIRT_LIMO = 30 ether;         // virtual native reserve (curve depth / price anchor)
    uint256 public constant K = VIRT_LIMO * SUPPLY;       // constant product (same for every pool)
    uint256 public constant GRAD_LIMO = 85 ether;         // graduation milestone (pump.fun-style "bonded" target)
    uint256 public constant FEE_BPS = 100;                // 1% trade fee, taken on each buy and sell
    uint256 public constant CREATOR_FEE_BPS = 50;         // of FEE_BPS: half to the token's creator, half to treasury
    uint256 public constant DEV_BUY_CAP_BPS = 2000;       // creator's launch buy may take at most 20% of supply (anti-snipe)

    address public immutable treasury; // collects the protocol half of every fee

    struct Pool {
        bool exists;
        bool graduated;     // crossed GRAD_LIMO (milestone only — trading continues)
        address creator;
        uint256 realLimo;   // native LIMO actually held for this token (post-fee)
        uint256 createdAt;
    }

    mapping(address => Pool) public pools;
    mapping(address => uint256) public pendingFees; // pull-pattern fee accrual (creator + treasury)
    address[] public allTokens;

    event Launched(address indexed token, address indexed creator, string name, string symbol, uint256 supply, uint256 createdAt);
    event Trade(address indexed token, address indexed trader, bool isBuy, uint256 limoAmount, uint256 tokenAmount, uint256 priceX1e18, uint256 ts);
    event Graduated(address indexed token, uint256 realLimo, uint256 ts);
    event FeesClaimed(address indexed who, uint256 amount);

    constructor() {
        treasury = msg.sender;
    }

    function tokenCount() external view returns (uint256) {
        return allTokens.length;
    }

    /// @notice List a page of launched tokens, newest first.
    function tokensPage(uint256 offset, uint256 limit) external view returns (address[] memory out) {
        uint256 n = allTokens.length;
        if (offset >= n) return new address[](0);
        uint256 end = offset + limit;
        if (end > n) end = n;
        out = new address[](end - offset);
        for (uint256 i = 0; i < out.length; i++) {
            out[i] = allTokens[n - 1 - offset - i]; // newest first
        }
    }

    /// @notice Create a new token + curve. Optional msg.value does an initial buy.
    /// @dev The initial buy carries no slippage/deadline guard (the token is born in
    ///      this same tx, so there is no pool state to front-run) but IS capped to
    ///      DEV_BUY_CAP_BPS of supply so a creator can't snipe their own launch.
    function create(string calldata name_, string calldata symbol_) external payable returns (address token) {
        SqueezeToken t = new SqueezeToken(name_, symbol_, SUPPLY, address(this));
        token = address(t);
        pools[token] = Pool({ exists: true, graduated: false, creator: msg.sender, realLimo: 0, createdAt: block.timestamp });
        allTokens.push(token);
        emit Launched(token, msg.sender, name_, symbol_, SUPPLY, block.timestamp);
        if (msg.value > 0) _buy(token, msg.sender, 0, true);
    }

    /// @notice Buy off the curve with native LIMO.
    /// @param minTokensOut Revert if fewer than this many tokens would be received
    ///        (mainnet-style on-chain slippage protection — enforced at mining time,
    ///        not just in the client, so it holds even if other trades land first).
    /// @param deadline Revert if mined after this unix timestamp (staleness guard).
    function buy(address token, uint256 minTokensOut, uint256 deadline) external payable {
        require(block.timestamp <= deadline, "expired");
        _buy(token, msg.sender, minTokensOut, false);
    }

    function _buy(address token, address to, uint256 minTokensOut, bool isCreate) internal {
        Pool storage p = pools[token];
        require(p.exists, "no pool");
        require(msg.value > 0, "no value");
        uint256 fee = msg.value * FEE_BPS / 10000;
        uint256 inAmt = msg.value - fee;
        uint256 resLimo = VIRT_LIMO + p.realLimo;
        uint256 resTok = SqueezeToken(token).balanceOf(address(this));
        // Round the new token reserve UP so the buyer's `out` is rounded DOWN — dust
        // accrues to the pool, never the trader (the Uniswap-safe direction).
        uint256 denom = resLimo + inAmt;
        uint256 newResTok = (K + denom - 1) / denom;
        uint256 out = resTok - newResTok;
        require(out > 0, "dust"); // never take LIMO and hand back zero tokens
        if (isCreate) require(out <= SUPPLY * DEV_BUY_CAP_BPS / 10000, "dev cap");
        require(out >= minTokensOut, "slippage");
        p.realLimo += inAmt;
        _accrue(p.creator, fee);
        SqueezeToken(token).transfer(to, out);
        emit Trade(token, to, true, msg.value, out, _price(token), block.timestamp);
        if (!p.graduated && p.realLimo >= GRAD_LIMO) {
            p.graduated = true;
            emit Graduated(token, p.realLimo, block.timestamp);
        }
    }

    /// @notice Sell `amount` tokens back to the curve for native LIMO. No approval needed.
    /// @param minLimoOut Revert if less than this much LIMO would be received (on-chain slippage guard, fee-inclusive).
    /// @param deadline Revert if mined after this unix timestamp (staleness guard).
    function sell(address token, uint256 amount, uint256 minLimoOut, uint256 deadline) external {
        require(block.timestamp <= deadline, "expired");
        Pool storage p = pools[token];
        require(p.exists, "no pool");
        require(amount > 0, "no amount");
        SqueezeToken t = SqueezeToken(token);
        require(t.transferFrom(msg.sender, address(this), amount), "pull");
        uint256 resLimo = VIRT_LIMO + p.realLimo;
        uint256 resTok = t.balanceOf(address(this)); // already includes pulled `amount`
        // Round the new LIMO reserve UP so the seller's gross is rounded DOWN (pool-favoured).
        uint256 newResLimo = (K + resTok - 1) / resTok;
        uint256 gross = resLimo - newResLimo;
        require(gross <= p.realLimo, "drain"); // never touch the virtual reserve
        uint256 fee = gross * FEE_BPS / 10000;
        uint256 netOut = gross - fee;
        require(netOut >= minLimoOut, "slippage");
        // checks-effects-interactions: shrink the pool + book the fee BEFORE paying out.
        p.realLimo -= gross;
        _accrue(p.creator, fee);
        (bool ok, ) = payable(msg.sender).call{ value: netOut }("");
        require(ok, "send");
        emit Trade(token, msg.sender, false, netOut, amount, _price(token), block.timestamp);
    }

    /// @dev Split a collected fee between the token's creator and the protocol treasury,
    ///      accrued for pull-payment (no external call on the hot path).
    function _accrue(address creator, uint256 fee) internal {
        if (fee == 0) return;
        uint256 creatorCut = fee * CREATOR_FEE_BPS / FEE_BPS;
        pendingFees[creator] += creatorCut;
        pendingFees[treasury] += fee - creatorCut;
    }

    /// @notice Withdraw accrued trade fees (creators and the treasury).
    function claimFees() external {
        uint256 amt = pendingFees[msg.sender];
        require(amt > 0, "none");
        pendingFees[msg.sender] = 0;
        (bool ok, ) = payable(msg.sender).call{ value: amt }("");
        require(ok, "send");
        emit FeesClaimed(msg.sender, amt);
    }

    function _price(address token) internal view returns (uint256) {
        uint256 resTok = SqueezeToken(token).balanceOf(address(this));
        if (resTok == 0) return 0;
        return (VIRT_LIMO + pools[token].realLimo) * 1e18 / resTok; // LIMO per token, 1e18-scaled
    }

    function price(address token) external view returns (uint256) {
        return _price(token);
    }

    function quoteBuy(address token, uint256 limoIn) external view returns (uint256 tokensOut) {
        Pool storage p = pools[token];
        if (!p.exists) return 0;
        uint256 inAmt = limoIn - (limoIn * FEE_BPS / 10000); // fee-inclusive, matches buy()
        uint256 resLimo = VIRT_LIMO + p.realLimo;
        uint256 resTok = SqueezeToken(token).balanceOf(address(this));
        uint256 denom = resLimo + inAmt;
        tokensOut = resTok - (K + denom - 1) / denom;
    }

    function quoteSell(address token, uint256 tokIn) external view returns (uint256 limoOut) {
        Pool storage p = pools[token];
        if (!p.exists) return 0;
        uint256 resLimo = VIRT_LIMO + p.realLimo;
        uint256 resTok = SqueezeToken(token).balanceOf(address(this));
        uint256 denom = resTok + tokIn;
        uint256 gross = resLimo - (K + denom - 1) / denom;
        limoOut = gross - (gross * FEE_BPS / 10000); // fee-inclusive, matches sell()
    }

    /// @notice Everything the UI needs about a pool in one call.
    function poolInfo(address token)
        external
        view
        returns (
            address creator,
            uint256 realLimo,
            uint256 reserveTok,
            uint256 priceX1e18,
            uint256 marketCapX1e18,
            uint256 createdAt,
            bool graduated,
            uint256 gradLimo
        )
    {
        Pool storage p = pools[token];
        creator = p.creator;
        realLimo = p.realLimo;
        reserveTok = SqueezeToken(token).balanceOf(address(this));
        priceX1e18 = _price(token);
        marketCapX1e18 = priceX1e18 * SUPPLY / 1e18; // notional, in LIMO
        createdAt = p.createdAt;
        graduated = p.graduated;
        gradLimo = GRAD_LIMO;
    }
}

ABI

26
[
  {
    "type": "constructor",
    "inputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "FeesClaimed",
    "type": "event",
    "inputs": [
      {
        "name": "who",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Graduated",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "realLimo",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "ts",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Launched",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "creator",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "name",
        "type": "string",
        "indexed": false,
        "internalType": "string"
      },
      {
        "name": "symbol",
        "type": "string",
        "indexed": false,
        "internalType": "string"
      },
      {
        "name": "supply",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "createdAt",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "Trade",
    "type": "event",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "trader",
        "type": "address",
        "indexed": true,
        "internalType": "address"
      },
      {
        "name": "isBuy",
        "type": "bool",
        "indexed": false,
        "internalType": "bool"
      },
      {
        "name": "limoAmount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "tokenAmount",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "priceX1e18",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      },
      {
        "name": "ts",
        "type": "uint256",
        "indexed": false,
        "internalType": "uint256"
      }
    ],
    "anonymous": false
  },
  {
    "name": "CREATOR_FEE_BPS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "DEV_BUY_CAP_BPS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "FEE_BPS",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "GRAD_LIMO",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "K",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "SUPPLY",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "VIRT_LIMO",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "allTokens",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "buy",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "minTokensOut",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "deadline",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "payable"
  },
  {
    "name": "claimFees",
    "type": "function",
    "inputs": [],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "create",
    "type": "function",
    "inputs": [
      {
        "name": "name_",
        "type": "string",
        "internalType": "string"
      },
      {
        "name": "symbol_",
        "type": "string",
        "internalType": "string"
      }
    ],
    "outputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "payable"
  },
  {
    "name": "pendingFees",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "poolInfo",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "creator",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "realLimo",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "reserveTok",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "priceX1e18",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "marketCapX1e18",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "createdAt",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "graduated",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "gradLimo",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "pools",
    "type": "function",
    "inputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "exists",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "graduated",
        "type": "bool",
        "internalType": "bool"
      },
      {
        "name": "creator",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "realLimo",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "createdAt",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "price",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      }
    ],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "quoteBuy",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "limoIn",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "tokensOut",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "quoteSell",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "tokIn",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "limoOut",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "sell",
    "type": "function",
    "inputs": [
      {
        "name": "token",
        "type": "address",
        "internalType": "address"
      },
      {
        "name": "amount",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "minLimoOut",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "deadline",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [],
    "stateMutability": "nonpayable"
  },
  {
    "name": "tokenCount",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "tokensPage",
    "type": "function",
    "inputs": [
      {
        "name": "offset",
        "type": "uint256",
        "internalType": "uint256"
      },
      {
        "name": "limit",
        "type": "uint256",
        "internalType": "uint256"
      }
    ],
    "outputs": [
      {
        "name": "out",
        "type": "address[]",
        "internalType": "address[]"
      }
    ],
    "stateMutability": "view"
  },
  {
    "name": "treasury",
    "type": "function",
    "inputs": [],
    "outputs": [
      {
        "name": "",
        "type": "address",
        "internalType": "address"
      }
    ],
    "stateMutability": "view"
  }
]