// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IPonsFactory} from "../../src/pons/IPons.sol"; /// Stand-ins for pons v2, matching what the live contracts were observed to do on /// Robinhood Chain mainnet (see script/simulate-pons.sh): a 1B token minted to its /// curve, a 1.68 ETH virtual quote reserve, a 1% fee taken from the input on buys and /// from the output on sells, sells pulled with transferFrom, graduation at 4.2 ETH. /// Used by the tests and by script/DeployLocalPons.s.sol for local UI work. contract MockPonsToken is ERC20 { string public logo; string public description; IPonsFactory.Socials private _socials; constructor(IPonsFactory.TokenParams memory p, address curve) ERC20(p.name, p.symbol) { logo = p.logo; description = p.description; _socials = p.socials; _mint(curve, 1_000_000_000 ether); } function socials() external view returns (IPonsFactory.Socials memory) { return _socials; } } contract MockPonsCurve { event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax); event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax); MockPonsToken public token; uint256 public quoteReserve = 1.68 ether; uint256 public tokenReserve = 1_000_000_000 ether; uint256 public realQuoteReserve; uint256 public constant phantomQuote = 1.68 ether; uint256 public constant feeBps = 100; uint256 public constant graduationThreshold = 4.2 ether; uint256 public creatorTaxBps; /// Test knob: hand back this share of each buy (bps) to the caller, like a curve filling up. uint256 public refundBps; function init(MockPonsToken t, uint256 taxBps, uint256 refundBps_) external { token = t; creatorTaxBps = taxBps; refundBps = refundBps_; } function graduated() external view returns (bool) { return realQuoteReserve >= graduationThreshold; } function currentSnipeTaxBps(address) external pure returns (uint256) { return 0; } function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) external payable returns (uint256 out) { require(msg.value == quoteIn, "value"); uint256 refund = (quoteIn * refundBps) / 10_000; uint256 used = quoteIn - refund; uint256 fee = (used * feeBps) / 10_000; uint256 tax = (used * creatorTaxBps) / 10_000; uint256 net = used - fee - tax; out = (tokenReserve * net) / (quoteReserve + net); require(out >= minTokensOut, "slippage"); quoteReserve += net; realQuoteReserve += net; tokenReserve -= out; token.transfer(recipient, out); emit CurveBuy(msg.sender, recipient, used, out, fee, tax); if (refund > 0) payable(msg.sender).transfer(refund); } function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) external returns (uint256 out) { token.transferFrom(msg.sender, address(this), tokensIn); uint256 gross = (quoteReserve * tokensIn) / (tokenReserve + tokensIn); uint256 fee = (gross * feeBps) / 10_000; uint256 tax = (gross * creatorTaxBps) / 10_000; out = gross - fee - tax; require(out >= minQuoteOut, "slippage"); quoteReserve -= gross; realQuoteReserve -= gross; tokenReserve += tokensIn; emit CurveSell(msg.sender, recipient, tokensIn, out, fee, tax); payable(recipient).transfer(out); } receive() external payable {} } contract MockPonsFactory { uint256 public launchFee = 0.0005 ether; uint256 public constant maxCreatorTaxBps = 1000; bytes32 public constant ECON = keccak256("econ"); IPonsFactory.TokenParams public last; address public lastLauncher; MockPonsCurve public lastCurve; address[] private _lastExemptions; mapping(bytes32 => bool) public saltUsed; /// Test knob: new curves refund this share of each buy. uint256 public nextRefundBps; function setNextRefundBps(uint256 bps) external { nextRefundBps = bps; } function lastExemptions() external view returns (address[] memory) { return _lastExemptions; } function canLaunch(address) external pure returns (bool) { return true; } function previewLaunchEconomics(uint256, address) external pure returns (bytes32) { return ECON; } function launchToken(IPonsFactory.TokenParams calldata p, uint256, address pairToken, address[] calldata exemptions) external payable returns (address token, address curve) { require(msg.value == launchFee, "fee"); require(pairToken == address(0), "pair"); require(p.expectedEconomics == ECON, "econ"); require(p.creatorTaxBps <= maxCreatorTaxBps, "tax"); bytes32 key = keccak256(abi.encode(msg.sender, p.salt)); require(!saltUsed[key], "salt"); saltUsed[key] = true; last = p; lastLauncher = msg.sender; _lastExemptions = exemptions; MockPonsCurve c = new MockPonsCurve(); MockPonsToken t = new MockPonsToken(p, address(c)); c.init(t, p.creatorTaxBps, nextRefundBps); lastCurve = c; return (address(t), address(c)); } }