// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; import "../../contracts/dex/DODOPMMIntegration.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) { _mint(msg.sender, 1_000_000 ether); } } contract MockPoolFactory { address public nextPool; function setNextPool(address pool) external { nextPool = pool; } function createDVM( address, address, uint256, uint256, uint256, bool ) external view returns (address dvm) { return nextPool; } } contract MockBasicDodoPool { address public immutable baseToken; address public immutable quoteToken; constructor(address baseToken_, address quoteToken_) { baseToken = baseToken_; quoteToken = quoteToken_; } function _BASE_TOKEN_() external view returns (address) { return baseToken; } function _QUOTE_TOKEN_() external view returns (address) { return quoteToken; } function getVaultReserve() external pure returns (uint256, uint256) { return (1_000_000, 1_000_000); } function getMidPrice() external pure returns (uint256) { return 1e18; } function _QUOTE_RESERVE_() external pure returns (uint256) { return 1_000_000; } function _BASE_RESERVE_() external pure returns (uint256) { return 1_000_000; } } contract MockStandardDodoPool is MockBasicDodoPool { constructor(address baseToken_, address quoteToken_) MockBasicDodoPool(baseToken_, quoteToken_) {} function querySellBase(address, uint256 payBaseAmount) external pure returns (uint256, uint256) { return (payBaseAmount, 0); } function querySellQuote(address, uint256 payQuoteAmount) external pure returns (uint256, uint256) { return (payQuoteAmount, 0); } function buyShares(address) external pure returns (uint256, uint256, uint256) { return (0, 0, 1); } function sellBase(address) external pure returns (uint256) { return 0; } function sellQuote(address) external pure returns (uint256) { return 0; } } contract MockPartialDodoPool is MockBasicDodoPool { constructor(address baseToken_, address quoteToken_) MockBasicDodoPool(baseToken_, quoteToken_) {} function querySellBase(address, uint256) external pure returns (uint256, uint256) { revert("query disabled"); } function querySellQuote(address, uint256) external pure returns (uint256, uint256) { revert("query disabled"); } function buyShares(address) external pure returns (uint256, uint256, uint256) { return (0, 0, 1); } function sellBase(address) external pure returns (uint256) { return 0; } function sellQuote(address) external pure returns (uint256) { return 0; } } contract DODOPMMIntegrationTest is Test { DODOPMMIntegration public integration; MockPoolFactory public dvm; address public dodoApprove = address(0xD0D0); MockERC20 public officialUSDT; MockERC20 public officialUSDC; MockERC20 public compliantUSDT; MockERC20 public compliantUSDC; address public admin = address(0xAD); function setUp() public { officialUSDT = new MockERC20("USDT", "USDT"); officialUSDC = new MockERC20("USDC", "USDC"); compliantUSDT = new MockERC20("cUSDT", "cUSDT"); compliantUSDC = new MockERC20("cUSDC", "cUSDC"); dvm = new MockPoolFactory(); officialUSDT.transfer(admin, 1_000 ether); officialUSDC.transfer(admin, 1_000 ether); compliantUSDT.transfer(admin, 1_000 ether); compliantUSDC.transfer(admin, 1_000 ether); integration = new DODOPMMIntegration( admin, address(dvm), dodoApprove, address(officialUSDT), address(officialUSDC), address(compliantUSDT), address(compliantUSDC) ); } function testCreatePoolGeneric() public { address baseToken = address(compliantUSDT); address quoteToken = address(officialUSDC); MockStandardDodoPool poolContract = new MockStandardDodoPool(baseToken, quoteToken); dvm.setNextPool(address(poolContract)); vm.prank(admin); address pool = integration.createPool(baseToken, quoteToken, 3, 1e18, 0.5e18, true); assertEq(pool, address(poolContract)); assertEq(integration.pools(baseToken, quoteToken), address(poolContract)); assertEq(integration.pools(quoteToken, baseToken), address(poolContract)); assertTrue(integration.isRegisteredPool(address(poolContract))); assertFalse(integration.hasStandardPoolSurface(address(poolContract))); } function testCreatePoolRevertsSameToken() public { vm.prank(admin); vm.expectRevert("DODOPMMIntegration: same token"); integration.createPool(address(officialUSDT), address(officialUSDT), 3, 1e18, 0.5e18, true); } function testCreatePoolRevertsZeroBase() public { vm.prank(admin); vm.expectRevert("DODOPMMIntegration: zero base"); integration.createPool(address(0), address(officialUSDT), 3, 1e18, 0.5e18, true); } function testCreatePoolRevertsWithoutBasicSurface() public { dvm.setNextPool(address(0xBEEF)); vm.prank(admin); vm.expectRevert(); integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false); } function testImportExistingPoolRecordsMappings() public { address baseToken = address(officialUSDT); address quoteToken = address(compliantUSDC); MockStandardDodoPool pool = new MockStandardDodoPool(baseToken, quoteToken); vm.prank(admin); integration.importExistingPool(address(pool), baseToken, quoteToken, 3, 1e18, 0.5e18, false); assertEq(integration.pools(baseToken, quoteToken), address(pool)); assertEq(integration.pools(quoteToken, baseToken), address(pool)); assertTrue(integration.isRegisteredPool(address(pool))); DODOPMMIntegration.PoolConfig memory config = integration.getPoolConfig(address(pool)); assertEq(config.baseToken, baseToken); assertEq(config.quoteToken, quoteToken); } function testImportExistingPoolAcceptsReverseHintAndNormalizes() public { address baseToken = address(officialUSDT); address quoteToken = address(compliantUSDC); MockStandardDodoPool pool = new MockStandardDodoPool(baseToken, quoteToken); vm.prank(admin); integration.importExistingPool(address(pool), quoteToken, baseToken, 3, 1e18, 0.5e18, false); DODOPMMIntegration.PoolConfig memory config = integration.getPoolConfig(address(pool)); assertEq(config.baseToken, baseToken); assertEq(config.quoteToken, quoteToken); } function testImportExistingPoolRevertsOnMismatch() public { MockStandardDodoPool pool = new MockStandardDodoPool(address(officialUSDT), address(compliantUSDC)); vm.prank(admin); vm.expectRevert("DODOPMMIntegration: pool token mismatch"); integration.importExistingPool(address(pool), address(officialUSDT), address(compliantUSDT), 3, 1e18, 0.5e18, false); } function testAddLiquidityMarksStandardSurface() public { MockStandardDodoPool pool = new MockStandardDodoPool(address(compliantUSDT), address(officialUSDC)); dvm.setNextPool(address(pool)); vm.startPrank(admin); address createdPool = integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false); compliantUSDT.approve(address(integration), 100); officialUSDC.approve(address(integration), 100); integration.addLiquidity(createdPool, 100, 100); vm.stopPrank(); assertTrue(integration.hasStandardPoolSurface(createdPool)); } function testAddLiquidityRevertsForPartialSurfacePool() public { MockPartialDodoPool pool = new MockPartialDodoPool(address(compliantUSDT), address(officialUSDC)); dvm.setNextPool(address(pool)); vm.startPrank(admin); address createdPool = integration.createPool(address(compliantUSDT), address(officialUSDC), 3, 1e18, 0.5e18, false); compliantUSDT.approve(address(integration), 100); officialUSDC.approve(address(integration), 100); vm.expectRevert("DODOPMMIntegration: pool missing standard DODO surface"); integration.addLiquidity(createdPool, 100, 100); vm.stopPrank(); } function testRefreshPoolSurfaceMarksImportedStandardPool() public { MockStandardDodoPool pool = new MockStandardDodoPool(address(compliantUSDT), address(compliantUSDC)); vm.prank(admin); integration.importExistingPool(address(pool), address(compliantUSDT), address(compliantUSDC), 3, 1e18, 0.5e18, false); vm.prank(admin); bool standardSurface = integration.refreshPoolSurface(address(pool)); assertTrue(standardSurface); assertTrue(integration.hasStandardPoolSurface(address(pool))); } }