// Sources flattened with hardhat v2.29.1 https://hardhat.org // SPDX-License-Identifier: MIT // File @openzeppelin/contracts/utils/StorageSlot.sol@v5.6.1 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.20; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC-1967 implementation slot: * ```solidity * contract ERC1967 { * // Define the slot. Alternatively, use the SlotDerivation library to derive the slot. * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(newImplementation.code.length > 0); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * TIP: Consider using this library along with {SlotDerivation}. */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct Int256Slot { int256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `Int256Slot` with member `value` located at `slot`. */ function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns a `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } /** * @dev Returns a `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { assembly ("memory-safe") { r.slot := store.slot } } } // File @openzeppelin/contracts/utils/ReentrancyGuard.sol@v5.6.1 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.5.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at, * consider using {ReentrancyGuardTransient} instead. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. * * IMPORTANT: Deprecated. This storage-based reentrancy guard will be removed and replaced * by the {ReentrancyGuardTransient} variant in v6.0. * * @custom:stateless */ abstract contract ReentrancyGuard { using StorageSlot for bytes32; // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant REENTRANCY_GUARD_STORAGE = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } /** * @dev A `view` only version of {nonReentrant}. Use to block view functions * from being called, preventing reading from inconsistent contract state. * * CAUTION: This is a "view" modifier and does not change the reentrancy * status. Use it only on view functions. For payable or non-payable functions, * use the standard {nonReentrant} modifier instead. */ modifier nonReentrantView() { _nonReentrantBeforeView(); _; } function _nonReentrantBeforeView() private view { if (_reentrancyGuardEntered()) { revert ReentrancyGuardReentrantCall(); } } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED _nonReentrantBeforeView(); // Any calls to nonReentrant after this point will fail _reentrancyGuardStorageSlot().getUint256Slot().value = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _reentrancyGuardStorageSlot().getUint256Slot().value = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _reentrancyGuardStorageSlot().getUint256Slot().value == ENTERED; } function _reentrancyGuardStorageSlot() internal pure virtual returns (bytes32) { return REENTRANCY_GUARD_STORAGE; } } // File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.6.1 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol) pragma solidity >=0.4.16; /** * @dev Interface of the ERC-20 standard as defined in the ERC. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); } // File contracts/fanbeimao/FanBeiMaoGenesisSale.sol // Original license: SPDX_License_Identifier: MIT pragma solidity ^0.8.24; interface IAgentRegistry { function handleOf(address account) external view returns (string memory); function ownerOfHandle(string calldata handle) external view returns (address); } interface IPancakeRouter02 { function factory() external view returns (address); function addLiquidityETH( address token, uint256 amountTokenDesired, uint256 amountTokenMin, uint256 amountETHMin, address to, uint256 deadline ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); } interface IPancakeFactory { function getPair(address tokenA, address tokenB) external view returns (address pair); } /** * 翻倍猫 · 创世销售(Phase 1) * * 规则: * - 10,000 份:免费 2,000 份(每个 .ai Agent ID 1 份,每份 100,000 枚) * 付费 8,000 份(每个 Agent ID 最多 10 份,每份 0.001 BNB,每份 1,000,000 枚) * - 只有免费 2,000 份卖完后,付费才开启;卖不完就等 3 天后退款 * - 付费资金:80% 开池 / 10% 前 10 邀请人 / 10% 国库 * - 第 10,000 份自动开 PancakeSwap:6.4 BNB + 6.4e9 枚,LP 打黑洞 * - 开售 3 天未满 10,000 份:付费份额可退款 * - 代币由本合约托管,开池后买家自行 claim;退款成功后不再发币 * * 这个合约只负责创世销售;交易税、每周 Merkle 分红、开池后继续铸造属于 Phase 2。 */ contract FanBeiMaoGenesisSale is ReentrancyGuard { address public constant DEAD = 0x000000000000000000000000000000000000dEaD; uint256 public constant PRICE = 0.001 ether; uint256 public constant FREE_TOKENS = 100_000; uint256 public constant PAID_TOKENS = 1_000_000; uint256 public constant POOL_TOKENS = 6_400_000_000; uint256 public constant MAX_PER_AGENT = 10; uint256 public constant POOL_BPS = 8_000; uint256 public constant INVITER_BPS = 1_000; uint256 public constant TREASURY_BPS = 1_000; uint256 public constant REFUND_PERIOD = 3 days; IERC20 public immutable token; address payable public immutable treasury; address public immutable agentRegistry; address public immutable router; address public immutable wbnb; uint256 public immutable startAt; uint256 public immutable refundAt; uint256 public immutable freeCap; uint256 public immutable paidCap; uint256 public freeSold; uint256 public paidSold; uint256 public totalPaid; uint256 public totalOwed; uint256 public treasuryOwed; bool public poolSeeded; bool public poolPending; mapping(bytes32 => bool) public freeClaimedByKey; mapping(bytes32 => uint256) public paidByKey; mapping(address => uint256) public sharesOf; mapping(address => uint256) public tokensOwed; mapping(address => uint256) public paidBy; mapping(address => bool) public refunded; mapping(address => uint256) public invites; address[10] public topInviters; uint256[10] public topCounts; event FreeClaimed(address indexed buyer, bytes32 indexed key, uint256 tokens); event SharesBought(address indexed buyer, address indexed referrer, uint256 shares, uint256 paid, uint256 tokens); event InviteCounted(address indexed referrer, address indexed buyer, uint256 count); event PoolSeeded(address indexed pair, uint256 bnbAmount, uint256 tokenAmount, uint256 liquidity); event PoolPending(); event InviterPaid(address indexed inviter, uint256 amount); event TreasuryPaid(uint256 amount); event TokensClaimed(address indexed buyer, uint256 amount); event Refunded(address indexed buyer, uint256 amount); constructor( address token_, address payable treasury_, address agentRegistry_, address router_, address wbnb_, uint256 startAt_, uint256 freeCap_, uint256 paidCap_ ) { require(token_ != address(0), "token=0"); require(treasury_ != address(0), "treasury=0"); require(agentRegistry_ != address(0), "registry=0"); require(router_ != address(0), "router=0"); require(wbnb_ != address(0), "wbnb=0"); require(startAt_ > 0, "start=0"); require(freeCap_ > 0 && paidCap_ > 0, "cap=0"); require(freeCap_ + paidCap_ <= 10_000, "cap>10k"); token = IERC20(token_); treasury = treasury_; agentRegistry = agentRegistry_; router = router_; wbnb = wbnb_; startAt = startAt_; refundAt = startAt_ + REFUND_PERIOD; freeCap = freeCap_; paidCap = paidCap_; } /* ----------------------------- views ----------------------------- */ function totalShares() public view returns (uint256) { return freeCap + paidCap; } function totalSold() public view returns (uint256) { return freeSold + paidSold; } function isFreeOpen() public view returns (bool) { return block.timestamp >= startAt && block.timestamp < refundAt && !poolSeeded && freeSold < freeCap; } function isPaidOpen() public view returns (bool) { if (block.timestamp < startAt || block.timestamp >= refundAt || poolSeeded || paidSold >= paidCap) return false; return freeSold >= freeCap; } function agentKeyOf(address who) external view returns (bytes32) { return _agentKey(who); } function topInvitersList() external view returns (address[10] memory, uint256[10] memory) { return (topInviters, topCounts); } /* ----------------------------- agent id ----------------------------- */ function _agentKey(address who) internal view returns (bytes32) { if (agentRegistry == address(0)) { return bytes32(uint256(uint160(who))); } try IAgentRegistry(agentRegistry).handleOf(who) returns (string memory h) { if (bytes(h).length == 0) return bytes32(0); try IAgentRegistry(agentRegistry).ownerOfHandle(h) returns (address owner_) { if (owner_ != who) return bytes32(0); return keccak256(bytes(h)); } catch { return bytes32(0); } } catch { return bytes32(0); } } /* ----------------------------- mint ----------------------------- */ function claimFree() external nonReentrant { require(isFreeOpen(), "free closed"); bytes32 key = _agentKey(msg.sender); require(key != bytes32(0), "agent id required"); require(!freeClaimedByKey[key], "free claimed"); freeClaimedByKey[key] = true; freeSold += 1; sharesOf[msg.sender] += 1; tokensOwed[msg.sender] += FREE_TOKENS; totalOwed += FREE_TOKENS; emit FreeClaimed(msg.sender, key, FREE_TOKENS); } function buy(uint256 shares, address referrer) external payable nonReentrant { require(isPaidOpen(), "paid closed"); require(shares > 0 && shares <= MAX_PER_AGENT, "shares"); require(paidSold + shares <= paidCap, "sold out"); bytes32 key = _agentKey(msg.sender); require(key != bytes32(0), "agent id required"); require(paidByKey[key] + shares <= MAX_PER_AGENT, "quota"); uint256 cost = PRICE * shares; require(msg.value == cost, "bad value"); paidByKey[key] += shares; paidSold += shares; sharesOf[msg.sender] += shares; paidBy[msg.sender] += cost; totalPaid += cost; uint256 tokens = PAID_TOKENS * shares; tokensOwed[msg.sender] += tokens; totalOwed += tokens; if (referrer != address(0) && referrer != msg.sender) { invites[referrer] += 1; _updateTop(referrer); emit InviteCounted(referrer, msg.sender, invites[referrer]); } emit SharesBought(msg.sender, referrer, shares, cost, tokens); if (freeSold + paidSold >= totalShares()) { /* 开池约需 3.5M gas;买家 gas 不够时不要硬开,留给任何人 openPool() 重试 */ if (gasleft() >= 4_500_000) { _openPool(); } else { poolPending = true; emit PoolPending(); } } } /* ----------------------------- top inviters ----------------------------- */ function _updateTop(address who) internal { uint256 count = invites[who]; uint256 idx = type(uint256).max; for (uint256 i = 0; i < 10; i++) { if (topInviters[i] == who) { idx = i; break; } } if (idx != type(uint256).max) { topCounts[idx] = count; while (idx > 0 && topCounts[idx] > topCounts[idx - 1]) { address ta = topInviters[idx]; topInviters[idx] = topInviters[idx - 1]; topInviters[idx - 1] = ta; uint256 tc = topCounts[idx]; topCounts[idx] = topCounts[idx - 1]; topCounts[idx - 1] = tc; idx--; } return; } if (count <= topCounts[9]) return; topInviters[9] = who; topCounts[9] = count; uint256 j = 9; while (j > 0 && topCounts[j] > topCounts[j - 1]) { address ta = topInviters[j]; topInviters[j] = topInviters[j - 1]; topInviters[j - 1] = ta; uint256 tc = topCounts[j]; topCounts[j] = topCounts[j - 1]; topCounts[j - 1] = tc; j--; } } /* ----------------------------- pool ----------------------------- */ function openPool() external nonReentrant { require(freeSold + paidSold >= totalShares(), "not filled"); require(!poolSeeded, "seeded"); _openPool(); require(poolSeeded, "pool failed"); } function _openPool() internal { if (poolSeeded) return; if (freeSold + paidSold < totalShares()) return; require(token.balanceOf(address(this)) >= POOL_TOKENS + totalOwed, "token balance"); uint256 poolBnb = (totalPaid * POOL_BPS) / 10_000; uint256 inviterBnb = (totalPaid * INVITER_BPS) / 10_000; uint256 treasuryBnb = totalPaid - poolBnb - inviterBnb; require(token.approve(router, POOL_TOKENS), "approve"); try IPancakeRouter02(router).addLiquidityETH{value: poolBnb, gas: 4_000_000}( address(token), POOL_TOKENS, 0, 0, DEAD, block.timestamp + 600 ) returns (uint256, uint256, uint256 liquidity) { require(liquidity > 0, "no lp"); poolSeeded = true; poolPending = false; address pair = IPancakeFactory(IPancakeRouter02(router).factory()).getPair(address(token), wbnb); emit PoolSeeded(pair, poolBnb, POOL_TOKENS, liquidity); _payInviters(inviterBnb); treasuryOwed += treasuryBnb; uint256 bal = token.balanceOf(address(this)); if (bal > totalOwed) { token.transfer(treasury, bal - totalOwed); } } catch { poolPending = true; emit PoolPending(); } } function _payInviters(uint256 amount) internal { uint256 total; for (uint256 i = 0; i < 10; i++) total += topCounts[i]; if (total == 0) { treasuryOwed += amount; return; } uint256 paid; for (uint256 i = 0; i < 10; i++) { uint256 count = topCounts[i]; if (count == 0) continue; uint256 share = (amount * count) / total; if (share == 0) continue; (bool ok, ) = payable(topInviters[i]).call{value: share}(""); if (ok) { paid += share; emit InviterPaid(topInviters[i], share); } } if (amount > paid) treasuryOwed += amount - paid; } function withdrawTreasury() external nonReentrant { require(msg.sender == treasury, "treasury only"); uint256 amount = treasuryOwed; require(amount > 0, "nothing"); treasuryOwed = 0; (bool ok, ) = treasury.call{value: amount}(""); require(ok, "treasury xfer"); emit TreasuryPaid(amount); } /* ----------------------------- claims / refunds ----------------------------- */ function claimTokens() external nonReentrant { require(poolSeeded, "not open"); uint256 amount = tokensOwed[msg.sender]; require(amount > 0, "nothing"); tokensOwed[msg.sender] = 0; totalOwed -= amount; require(token.transfer(msg.sender, amount), "token xfer"); emit TokensClaimed(msg.sender, amount); } function refund() external nonReentrant { require(!poolSeeded, "pool live"); require(freeSold + paidSold < totalShares(), "filled"); require(block.timestamp >= refundAt, "too early"); uint256 amount = paidBy[msg.sender]; require(amount > 0 && !refunded[msg.sender], "nothing"); refunded[msg.sender] = true; paidBy[msg.sender] = 0; uint256 owed = tokensOwed[msg.sender]; if (owed > 0) { tokensOwed[msg.sender] = 0; totalOwed -= owed; } (bool ok, ) = msg.sender.call{value: amount}(""); require(ok, "refund xfer"); emit Refunded(msg.sender, amount); } }