// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title MockDVMPool * @notice Minimal mock of a DODO PMM pool so DODOPMMIntegration can deploy and create pools on Chain 138 * @dev Not for production; use real DODO DVM when available. Implements view and swap stubs. */ contract MockDVMPool { address public baseToken; address public quoteToken; uint256 public baseReserve; uint256 public quoteReserve; uint256 public midPrice; uint256 public totalSupply; mapping(address => uint256) private _shares; uint256 public constant _K_ = 0.5e18; // 50% slippage factor (DODO convention) uint256 public constant _LP_FEE_RATE_ = 3; // 0.03% in basis points constructor( address _baseToken, address _quoteToken, uint256 _initialPrice ) { baseToken = _baseToken; quoteToken = _quoteToken; midPrice = _initialPrice; baseReserve = 0; quoteReserve = 0; } function _BASE_TOKEN_() external view returns (address) { return baseToken; } function _QUOTE_TOKEN_() external view returns (address) { return quoteToken; } function _BASE_RESERVE_() external view returns (uint256) { return baseReserve; } function _QUOTE_RESERVE_() external view returns (uint256) { return quoteReserve; } function getVaultReserve() external view returns (uint256, uint256) { return (baseReserve, quoteReserve); } function balanceOf(address owner) external view returns (uint256) { return _shares[owner]; } function getMidPrice() external view returns (uint256) { return midPrice; } /// @notice Oracle price (same as mid for mock); enables MCP dodo_pmm_v2_like profile function getOraclePrice() external view returns (uint256) { return midPrice; } function querySellBase(address, uint256 amount) external view returns (uint256, uint256) { return ((amount * midPrice) / 1e18, 0); } function querySellQuote(address, uint256 amount) external view returns (uint256, uint256) { if (midPrice == 0) { return (0, 0); } return ((amount * 1e18) / midPrice, 0); } function sellBase(address to) external returns (uint256 receiveQuoteAmount) { uint256 baseBal = IERC20(baseToken).balanceOf(address(this)); require(baseBal >= baseReserve, "MockDVMPool: base"); uint256 baseInput = baseBal - baseReserve; if (baseInput == 0) return 0; (receiveQuoteAmount,) = this.querySellBase(address(0), baseInput); require(IERC20(quoteToken).transfer(to, receiveQuoteAmount), "MockDVMPool: q"); baseReserve = IERC20(baseToken).balanceOf(address(this)); quoteReserve = IERC20(quoteToken).balanceOf(address(this)); } function sellQuote(address to) external returns (uint256 receiveBaseAmount) { uint256 quoteBal = IERC20(quoteToken).balanceOf(address(this)); require(quoteBal >= quoteReserve, "MockDVMPool: quote"); uint256 quoteInput = quoteBal - quoteReserve; if (quoteInput == 0) return 0; (receiveBaseAmount,) = this.querySellQuote(address(0), quoteInput); require(IERC20(baseToken).transfer(to, receiveBaseAmount), "MockDVMPool: b"); baseReserve = IERC20(baseToken).balanceOf(address(this)); quoteReserve = IERC20(quoteToken).balanceOf(address(this)); } function buyShares(address to) external returns (uint256, uint256, uint256) { uint256 baseBal = IERC20(baseToken).balanceOf(address(this)); uint256 quoteBal = IERC20(quoteToken).balanceOf(address(this)); require(baseBal >= baseReserve && quoteBal >= quoteReserve, "MockDVMPool: reserve"); uint256 baseInput = baseBal - baseReserve; uint256 quoteInput = quoteBal - quoteReserve; require(baseInput > 0 && quoteInput > 0, "MockDVMPool: no input"); uint256 lpShare; if (totalSupply == 0) { lpShare = baseBal; require(lpShare > 2001, "MockDVMPool: MINT_AMOUNT_NOT_ENOUGH"); totalSupply = lpShare; _shares[address(0)] = 1001; lpShare -= 1001; _shares[to] += lpShare; } else { uint256 baseShares = (baseInput * totalSupply) / baseReserve; uint256 quoteShares = (quoteInput * totalSupply) / quoteReserve; lpShare = baseShares < quoteShares ? baseShares : quoteShares; totalSupply += lpShare; _shares[to] += lpShare; } baseReserve = baseBal; quoteReserve = quoteBal; return (baseInput, quoteInput, lpShare); } function sync() external { baseReserve = IERC20(baseToken).balanceOf(address(this)); quoteReserve = IERC20(quoteToken).balanceOf(address(this)); } }