{"sources":{"UnidentifiedFreeOffering.sol":{"content":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\n/// @notice Minimal view of the Binance Agent ID registry (SPACE ID based).\ninterface IAgentIdRegistry {\n    function balanceOf(address owner) external view returns (uint256);\n}\n\n/// @title Unidentified Free Offering (UFO)\n/// @notice A UFO-themed card-style token drop for AI agents, in ONE contract:\n///         ERC-20 + a \"share\" sale in the WNO pattern, with the LUMO permit\n///         gate on free claims.\n///         - 3,000 shares total, 1 share = 3,000,000,000 UFO (9 trillion supply,\n///           locked in this contract since deployment).\n///         - First 1,000 shares: 1 FREE share per Agent ID wallet, claimable\n///           only with an EIP-712 permit from the project gate signer. Permits\n///           are issued on the project website only after the claimer's human\n///           posts about the project on X and the post is checked.\n///         - Remaining shares: 0.001 BNB per share, max 20 per transaction.\n///           Holders may buy more shares after taking their free one.\n///         - The ENTIRE claim/purchase window lasts 2 days from deployment;\n///           after that the sale is closed forever.\n///         - Only Agent ID holders can claim or buy (same setup as WNO).\n///         - No admin functions, no tax, no re-mint. Sale proceeds are\n///           forwarded to the treasury in the same transaction. The gate\n///           signer can do nothing else: it cannot move funds or change rules.\ncontract UnidentifiedFreeOffering {\n    // ---------------- ERC-20 ----------------\n    string public constant name = \"Unidentified Free Offering\";\n    string public constant symbol = \"UFO\";\n    uint8 public constant decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    event Transfer(address indexed from, address indexed to, uint256 value);\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n\n    // ---------------- Sale rules ----------------\n    IAgentIdRegistry public immutable registry;\n    address public immutable treasury;\n    address public immutable gateSigner;\n\n    uint256 public constant TOKENS_PER_SHARE = 3_000_000_000 * 1e18;\n    uint256 public constant FREE_SHARES = 1000;\n    uint256 public constant MAX_SHARES = 3000;\n    uint256 public constant PRICE = 0.001 ether; // per paid share\n    uint256 public constant MAX_PER_TX = 20;\n    uint256 public constant SALE_WINDOW = 2 days;\n\n    uint256 public immutable saleStart;\n    uint256 public immutable saleEnd;\n\n    uint256 public totalShares;      // free + paid sold\n    uint256 public totalFreeShares;\n    uint256 public totalPaidShares;\n    uint256 public totalRaised;      // BNB forwarded to treasury\n\n    mapping(address => bool) public freeUsed;\n    mapping(address => uint256) public sharesOf;\n    mapping(address => address) public referrerOf;\n\n    event MintShares(\n        address indexed buyer,\n        uint256 shares,\n        uint256 freeShares,\n        uint256 paidShares,\n        uint256 cost,\n        address referrer\n    );\n\n    bool private _locked;\n    modifier nonReentrant() {\n        require(!_locked, \"reentrant\");\n        _locked = true;\n        _;\n        _locked = false;\n    }\n\n    // ---------------- EIP-712 ----------------\n    bytes32 private immutable _domainSeparator;\n    // keccak256(\"Permit(address wallet,uint256 deadline)\")\n    bytes32 private constant _PERMIT_TYPEHASH = keccak256(\"Permit(address wallet,uint256 deadline)\");\n\n    constructor(address agentIdRegistry, address treasury_, address gateSigner_) {\n        require(agentIdRegistry != address(0), \"registry 0\");\n        require(treasury_ != address(0), \"treasury 0\");\n        require(gateSigner_ != address(0), \"signer 0\");\n        registry = IAgentIdRegistry(agentIdRegistry);\n        treasury = treasury_; // explicit: deployment goes through the CREATE2 proxy, never use msg.sender\n        gateSigner = gateSigner_;\n        saleStart = block.timestamp;\n        saleEnd = block.timestamp + SALE_WINDOW;\n        totalSupply = MAX_SHARES * TOKENS_PER_SHARE; // 9,000,000,000,000 UFO\n        balanceOf[address(this)] = totalSupply;\n        emit Transfer(address(0), address(this), totalSupply);\n        _domainSeparator = keccak256(\n            abi.encode(\n                keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                keccak256(bytes(\"UnidentifiedFreeOffering\")),\n                keccak256(bytes(\"1\")),\n                block.chainid,\n                address(this)\n            )\n        );\n    }\n\n    // ---------------- ERC-20 functions ----------------\n    function transfer(address to, uint256 value) external returns (bool) {\n        _transfer(msg.sender, to, value);\n        return true;\n    }\n\n    function approve(address spender, uint256 value) external returns (bool) {\n        allowance[msg.sender][spender] = value;\n        emit Approval(msg.sender, spender, value);\n        return true;\n    }\n\n    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 allowed = allowance[from][msg.sender];\n        require(allowed >= value, \"allowance\");\n        if (allowed != type(uint256).max) {\n            allowance[from][msg.sender] = allowed - value;\n        }\n        _transfer(from, to, value);\n        return true;\n    }\n\n    function _transfer(address from, address to, uint256 value) internal {\n        require(to != address(0), \"to 0\");\n        require(balanceOf[from] >= value, \"balance\");\n        balanceOf[from] -= value;\n        balanceOf[to] += value;\n        emit Transfer(from, to, value);\n    }\n\n    // ---------------- Agent ID gate ----------------\n    function isAgent(address wallet) public view returns (bool) {\n        try registry.balanceOf(wallet) returns (uint256 n) {\n            return n > 0;\n        } catch {\n            return false;\n        }\n    }\n\n    function saleOpen() public view returns (bool) {\n        return block.timestamp <= saleEnd;\n    }\n\n    // ---------------- Free claim (permit-gated) ----------------\n    /// @notice Claim the 1 free share for this Agent ID wallet. Needs a gate\n    ///         permit (EIP-712 signature by gateSigner over (msg.sender,\n    ///         deadline)), issued after a verified X post.\n    function claimFree(uint256 deadline, bytes calldata signature, address referrer)\n        external\n        nonReentrant\n    {\n        require(saleOpen(), \"sale closed\");\n        require(totalFreeShares < FREE_SHARES, \"free quota gone\");\n        require(totalShares < MAX_SHARES, \"sold out\");\n        require(!freeUsed[msg.sender], \"free already used\");\n        require(isAgent(msg.sender), \"agent id only\");\n        require(block.timestamp <= deadline, \"permit expired\");\n        require(_permitValid(msg.sender, deadline, signature), \"bad permit\");\n\n        freeUsed[msg.sender] = true;\n        totalFreeShares += 1;\n        totalShares += 1;\n        sharesOf[msg.sender] += 1;\n        _bindReferrer(referrer);\n\n        balanceOf[address(this)] -= TOKENS_PER_SHARE;\n        balanceOf[msg.sender] += TOKENS_PER_SHARE;\n        emit Transfer(address(this), msg.sender, TOKENS_PER_SHARE);\n        emit MintShares(msg.sender, 1, 1, 0, 0, referrer);\n    }\n\n    function _permitValid(address wallet, uint256 deadline, bytes calldata signature)\n        internal\n        view\n        returns (bool)\n    {\n        if (signature.length != 65) return false;\n        bytes32 r;\n        bytes32 s;\n        uint8 v;\n        assembly {\n            r := calldataload(signature.offset)\n            s := calldataload(add(signature.offset, 32))\n            v := byte(0, calldataload(add(signature.offset, 64)))\n        }\n        // reject malleable signatures\n        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {\n            return false;\n        }\n        if (v != 27 && v != 28) return false;\n        bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, wallet, deadline));\n        bytes32 digest = keccak256(abi.encodePacked(\"\\x19\\x01\", _domainSeparator, structHash));\n        address recovered = ecrecover(digest, v, r, s);\n        return recovered != address(0) && recovered == gateSigner;\n    }\n\n    // ---------------- Paid mint ----------------\n    /// @notice Cost in wei for `shares` paid shares.\n    function quote(uint256 shares) public view returns (uint256 cost) {\n        require(shares > 0 && shares <= MAX_PER_TX, \"shares 1..20\");\n        require(totalShares + shares <= MAX_SHARES, \"sold out\");\n        cost = shares * PRICE;\n    }\n\n    /// @notice Buy `shares` paid shares. Excess BNB refunded in the same tx.\n    function mint(uint256 shares, address referrer) external payable nonReentrant {\n        require(saleOpen(), \"sale closed\");\n        require(isAgent(msg.sender), \"agent id only\");\n        uint256 cost = quote(shares);\n        require(msg.value >= cost, \"underpaid\");\n\n        totalPaidShares += shares;\n        totalShares += shares;\n        sharesOf[msg.sender] += shares;\n        _bindReferrer(referrer);\n\n        uint256 tokenAmount = shares * TOKENS_PER_SHARE;\n        balanceOf[address(this)] -= tokenAmount;\n        balanceOf[msg.sender] += tokenAmount;\n        emit Transfer(address(this), msg.sender, tokenAmount);\n        emit MintShares(msg.sender, shares, 0, shares, cost, referrer);\n\n        totalRaised += cost;\n        (bool okTreasury, ) = treasury.call{value: cost}(\"\");\n        require(okTreasury, \"treasury failed\");\n        uint256 refund = msg.value - cost;\n        if (refund > 0) {\n            (bool okRefund, ) = msg.sender.call{value: refund}(\"\");\n            require(okRefund, \"refund failed\");\n        }\n    }\n\n    function _bindReferrer(address referrer) internal {\n        if (referrerOf[msg.sender] == address(0) && referrer != address(0) && referrer != msg.sender) {\n            referrerOf[msg.sender] = referrer;\n        }\n    }\n\n    // ---------------- Views for the site ----------------\n    function overview()\n        external\n        view\n        returns (\n            uint256 sharesSold,\n            uint256 freeSold,\n            uint256 paidSold,\n            uint256 freeRemaining,\n            uint256 sharesRemaining,\n            uint256 pricePerShare,\n            uint256 tokensPerShare,\n            uint256 saleEndsAt\n        )\n    {\n        sharesSold = totalShares;\n        freeSold = totalFreeShares;\n        paidSold = totalPaidShares;\n        freeRemaining = FREE_SHARES > totalFreeShares ? FREE_SHARES - totalFreeShares : 0;\n        sharesRemaining = MAX_SHARES - totalShares;\n        pricePerShare = PRICE;\n        tokensPerShare = TOKENS_PER_SHARE;\n        saleEndsAt = saleEnd;\n    }\n}\n"}},"abi":[{"type":"constructor","inputs":[{"name":"agentIdRegistry","type":"address","internalType":"address"},{"name":"treasury_","type":"address","internalType":"address"},{"name":"gateSigner_","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"name":"Approval","type":"event","inputs":[{"name":"owner","type":"address","indexed":true,"internalType":"address"},{"name":"spender","type":"address","indexed":true,"internalType":"address"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"MintShares","type":"event","inputs":[{"name":"buyer","type":"address","indexed":true,"internalType":"address"},{"name":"shares","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"freeShares","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"paidShares","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"cost","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"referrer","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"name":"Transfer","type":"event","inputs":[{"name":"from","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"value","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"name":"FREE_SHARES","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_PER_TX","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"MAX_SHARES","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"PRICE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"SALE_WINDOW","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"TOKENS_PER_SHARE","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"allowance","type":"function","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"approve","type":"function","inputs":[{"name":"spender","type":"address","internalType":"address"},{"name":"value","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"name":"balanceOf","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"claimFree","type":"function","inputs":[{"name":"deadline","type":"uint256","internalType":"uint256"},{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"referrer","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"name":"decimals","type":"function","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"name":"freeUsed","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"gateSigner","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"isAgent","type":"function","inputs":[{"name":"wallet","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"mint","type":"function","inputs":[{"name":"shares","type":"uint256","internalType":"uint256"},{"name":"referrer","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"payable"},{"name":"name","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"overview","type":"function","inputs":[],"outputs":[{"name":"sharesSold","type":"uint256","internalType":"uint256"},{"name":"freeSold","type":"uint256","internalType":"uint256"},{"name":"paidSold","type":"uint256","internalType":"uint256"},{"name":"freeRemaining","type":"uint256","internalType":"uint256"},{"name":"sharesRemaining","type":"uint256","internalType":"uint256"},{"name":"pricePerShare","type":"uint256","internalType":"uint256"},{"name":"tokensPerShare","type":"uint256","internalType":"uint256"},{"name":"saleEndsAt","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"quote","type":"function","inputs":[{"name":"shares","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"cost","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"referrerOf","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"name":"registry","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IAgentIdRegistry"}],"stateMutability":"view"},{"name":"saleEnd","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"saleOpen","type":"function","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"name":"saleStart","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"sharesOf","type":"function","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"symbol","type":"function","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"name":"totalFreeShares","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalPaidShares","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalRaised","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalShares","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"totalSupply","type":"function","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"name":"transfer","type":"function","inputs":[{"name":"to","type":"address","internalType":"address"},{"name":"value","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"name":"transferFrom","type":"function","inputs":[{"name":"from","type":"address","internalType":"address"},{"name":"to","type":"address","internalType":"address"},{"name":"value","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"nonpayable"},{"name":"treasury","type":"function","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"}],"matchId":"59031971","creationMatch":"exact_match","runtimeMatch":"exact_match","verifiedAt":"2026-11-10T15:06:52Z","match":"exact_match","chainId":"56","address":"0xE4059eA596392AcA14100af6CDC9481c330E2728"}