// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; /** * @title DODO PMM Pool Interface * @notice Simplified interface for DODO Proactive Market Maker pools * @dev Actual DODO interfaces may vary - this is a simplified version */ interface IDODOPMMPool { function _BASE_TOKEN_() external view returns (address); function _QUOTE_TOKEN_() external view returns (address); function sellBase(uint256 amount) external returns (uint256); function sellQuote(uint256 amount) external returns (uint256); function buyShares(address to) external returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare); function getVaultReserve() external view returns (uint256 baseReserve, uint256 quoteReserve); function getMidPrice() external view returns (uint256); function _QUOTE_RESERVE_() external view returns (uint256); function _BASE_RESERVE_() external view returns (uint256); } /** * @title DODO Vending Machine Interface * @notice Interface for creating DODO Vending Machine (DVM) pools */ interface IDODOVendingMachine { function createDVM( address baseToken, address quoteToken, uint256 lpFeeRate, uint256 i, uint256 k, bool isOpenTWAP ) external returns (address dvm); } /** * @title DODOPMMIntegration * @notice Integration contract for DODO PMM pools with CompliantUSDT/USDC * @dev Manages liquidity pools on DODO and provides swap functionality between * compliant tokens (cUSDT/cUSDC) and official tokens (USDT/USDC) * * This contract facilitates exchangeability between compliant and official tokens * through DODO's Proactive Market Maker algorithm, which maintains price stability * and provides efficient liquidity. */ contract DODOPMMIntegration is AccessControl, ReentrancyGuard { using SafeERC20 for IERC20; bytes32 public constant POOL_MANAGER_ROLE = keccak256("POOL_MANAGER_ROLE"); bytes32 public constant SWAP_OPERATOR_ROLE = keccak256("SWAP_OPERATOR_ROLE"); // DODO contracts address public immutable dodoVendingMachine; address public immutable dodoApprove; // DODO's approval contract for gas optimization // Token addresses address public immutable officialUSDT; // Official USDT on destination chain address public immutable officialUSDC; // Official USDC on destination chain address public immutable compliantUSDT; // cUSDT on Chain 138 address public immutable compliantUSDC; // cUSDC on Chain 138 // Pool mappings mapping(address => mapping(address => address)) public pools; // token0 => token1 => pool mapping(address => bool) public isRegisteredPool; // Pool configuration struct PoolConfig { address pool; address baseToken; address quoteToken; uint256 lpFeeRate; // Basis points (100 = 1%) uint256 i; // Initial price (1e18 = $1 for stablecoins) uint256 k; // Slippage factor (0.5 = 500000000000000000, lower = less slippage) bool isOpenTWAP; // Enable TWAP oracle for price discovery uint256 createdAt; } mapping(address => PoolConfig) public poolConfigs; address[] public allPools; event PoolCreated( address indexed pool, address indexed baseToken, address indexed quoteToken, address creator ); event LiquidityAdded( address indexed pool, address indexed provider, uint256 baseAmount, uint256 quoteAmount, uint256 lpShares ); event SwapExecuted( address indexed pool, address indexed tokenIn, address indexed tokenOut, uint256 amountIn, uint256 amountOut, address trader ); event PoolRemoved(address indexed pool); constructor( address admin, address dodoVendingMachine_, address dodoApprove_, address officialUSDT_, address officialUSDC_, address compliantUSDT_, address compliantUSDC_ ) { require(admin != address(0), "DODOPMMIntegration: zero admin"); require(dodoVendingMachine_ != address(0), "DODOPMMIntegration: zero DVM"); require(officialUSDT_ != address(0), "DODOPMMIntegration: zero USDT"); require(officialUSDC_ != address(0), "DODOPMMIntegration: zero USDC"); require(compliantUSDT_ != address(0), "DODOPMMIntegration: zero cUSDT"); require(compliantUSDC_ != address(0), "DODOPMMIntegration: zero cUSDC"); _grantRole(DEFAULT_ADMIN_ROLE, admin); _grantRole(POOL_MANAGER_ROLE, admin); _grantRole(SWAP_OPERATOR_ROLE, admin); dodoVendingMachine = dodoVendingMachine_; dodoApprove = dodoApprove_; officialUSDT = officialUSDT_; officialUSDC = officialUSDC_; compliantUSDT = compliantUSDT_; compliantUSDC = compliantUSDC_; } /** * @notice Create DODO PMM pool for cUSDT/USDT pair * @param lpFeeRate Liquidity provider fee rate (basis points, 3 = 0.03%) * @param initialPrice Initial price (1e18 = $1 for stablecoin pairs) * @param k Slippage factor (0.5e18 = 50%, lower = less slippage, higher = more slippage) * @param isOpenTWAP Enable TWAP oracle for price discovery */ function createCUSDTUSDTPool( uint256 lpFeeRate, uint256 initialPrice, uint256 k, bool isOpenTWAP ) external onlyRole(POOL_MANAGER_ROLE) returns (address pool) { require(pools[compliantUSDT][officialUSDT] == address(0), "DODOPMMIntegration: pool exists"); // Create DVM pool using DODO Vending Machine pool = IDODOVendingMachine(dodoVendingMachine).createDVM( compliantUSDT, // baseToken (cUSDT) officialUSDT, // quoteToken (USDT) lpFeeRate, // LP fee rate initialPrice, // Initial price k, // Slippage factor isOpenTWAP // Enable TWAP ); // Register pool pools[compliantUSDT][officialUSDT] = pool; pools[officialUSDT][compliantUSDT] = pool; isRegisteredPool[pool] = true; allPools.push(pool); poolConfigs[pool] = PoolConfig({ pool: pool, baseToken: compliantUSDT, quoteToken: officialUSDT, lpFeeRate: lpFeeRate, i: initialPrice, k: k, isOpenTWAP: isOpenTWAP, createdAt: block.timestamp }); emit PoolCreated(pool, compliantUSDT, officialUSDT, msg.sender); } /** * @notice Create DODO PMM pool for cUSDC/USDC pair * @param lpFeeRate Liquidity provider fee rate (basis points) * @param initialPrice Initial price (1e18 = $1) * @param k Slippage factor * @param isOpenTWAP Enable TWAP oracle */ function createCUSDCUSDCPool( uint256 lpFeeRate, uint256 initialPrice, uint256 k, bool isOpenTWAP ) external onlyRole(POOL_MANAGER_ROLE) returns (address pool) { require(pools[compliantUSDC][officialUSDC] == address(0), "DODOPMMIntegration: pool exists"); pool = IDODOVendingMachine(dodoVendingMachine).createDVM( compliantUSDC, officialUSDC, lpFeeRate, initialPrice, k, isOpenTWAP ); pools[compliantUSDC][officialUSDC] = pool; pools[officialUSDC][compliantUSDC] = pool; isRegisteredPool[pool] = true; allPools.push(pool); poolConfigs[pool] = PoolConfig({ pool: pool, baseToken: compliantUSDC, quoteToken: officialUSDC, lpFeeRate: lpFeeRate, i: initialPrice, k: k, isOpenTWAP: isOpenTWAP, createdAt: block.timestamp }); emit PoolCreated(pool, compliantUSDC, officialUSDC, msg.sender); } /** * @notice Add liquidity to a DODO PMM pool * @param pool Pool address * @param baseAmount Amount of base token to deposit * @param quoteAmount Amount of quote token to deposit */ function addLiquidity( address pool, uint256 baseAmount, uint256 quoteAmount ) external nonReentrant returns (uint256 baseShare, uint256 quoteShare, uint256 lpShare) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); require(baseAmount > 0 && quoteAmount > 0, "DODOPMMIntegration: zero amount"); PoolConfig memory config = poolConfigs[pool]; // Transfer tokens to pool (DODO pools handle their own token management) IERC20(config.baseToken).safeTransferFrom(msg.sender, pool, baseAmount); IERC20(config.quoteToken).safeTransferFrom(msg.sender, pool, quoteAmount); // Call buyShares on DODO pool to add liquidity (baseShare, quoteShare, lpShare) = IDODOPMMPool(pool).buyShares(msg.sender); emit LiquidityAdded(pool, msg.sender, baseAmount, quoteAmount, lpShare); } /** * @notice Swap cUSDT for official USDT via DODO PMM * @param pool Pool address * @param amountIn Amount of cUSDT to sell * @param minAmountOut Minimum amount of USDT to receive (slippage protection) */ function swapCUSDTForUSDT( address pool, uint256 amountIn, uint256 minAmountOut ) external nonReentrant returns (uint256 amountOut) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); require(poolConfigs[pool].baseToken == compliantUSDT, "DODOPMMIntegration: invalid pool"); require(amountIn > 0, "DODOPMMIntegration: zero amount"); // Transfer cUSDT to pool IERC20(compliantUSDT).safeTransferFrom(msg.sender, pool, amountIn); // Execute swap (sell base token) amountOut = IDODOPMMPool(pool).sellBase(amountIn); require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output"); emit SwapExecuted(pool, compliantUSDT, officialUSDT, amountIn, amountOut, msg.sender); } /** * @notice Swap official USDT for cUSDT via DODO PMM * @param pool Pool address * @param amountIn Amount of USDT to sell * @param minAmountOut Minimum amount of cUSDT to receive */ function swapUSDTForCUSDT( address pool, uint256 amountIn, uint256 minAmountOut ) external nonReentrant returns (uint256 amountOut) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); require(poolConfigs[pool].quoteToken == officialUSDT, "DODOPMMIntegration: invalid pool"); require(amountIn > 0, "DODOPMMIntegration: zero amount"); // Transfer USDT to pool IERC20(officialUSDT).safeTransferFrom(msg.sender, pool, amountIn); // Execute swap (sell quote token) amountOut = IDODOPMMPool(pool).sellQuote(amountIn); require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output"); emit SwapExecuted(pool, officialUSDT, compliantUSDT, amountIn, amountOut, msg.sender); } /** * @notice Swap cUSDC for official USDC via DODO PMM * @param pool Pool address * @param amountIn Amount of cUSDC to sell * @param minAmountOut Minimum amount of USDC to receive */ function swapCUSDCForUSDC( address pool, uint256 amountIn, uint256 minAmountOut ) external nonReentrant returns (uint256 amountOut) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); require(poolConfigs[pool].baseToken == compliantUSDC, "DODOPMMIntegration: invalid pool"); require(amountIn > 0, "DODOPMMIntegration: zero amount"); IERC20(compliantUSDC).safeTransferFrom(msg.sender, pool, amountIn); amountOut = IDODOPMMPool(pool).sellBase(amountIn); require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output"); emit SwapExecuted(pool, compliantUSDC, officialUSDC, amountIn, amountOut, msg.sender); } /** * @notice Swap official USDC for cUSDC via DODO PMM * @param pool Pool address * @param amountIn Amount of USDC to sell * @param minAmountOut Minimum amount of cUSDC to receive */ function swapUSDCForCUSDC( address pool, uint256 amountIn, uint256 minAmountOut ) external nonReentrant returns (uint256 amountOut) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); require(poolConfigs[pool].quoteToken == officialUSDC, "DODOPMMIntegration: invalid pool"); require(amountIn > 0, "DODOPMMIntegration: zero amount"); IERC20(officialUSDC).safeTransferFrom(msg.sender, pool, amountIn); amountOut = IDODOPMMPool(pool).sellQuote(amountIn); require(amountOut >= minAmountOut, "DODOPMMIntegration: insufficient output"); emit SwapExecuted(pool, officialUSDC, compliantUSDC, amountIn, amountOut, msg.sender); } /** * @notice Get current pool price * @param pool Pool address * @return price Current mid price (1e18 = $1 for stablecoins) */ function getPoolPrice(address pool) external view returns (uint256 price) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); price = IDODOPMMPool(pool).getMidPrice(); } /** * @notice Get pool reserves * @param pool Pool address * @return baseReserve Base token reserve * @return quoteReserve Quote token reserve */ function getPoolReserves(address pool) external view returns (uint256 baseReserve, uint256 quoteReserve) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); (baseReserve, quoteReserve) = IDODOPMMPool(pool).getVaultReserve(); } /** * @notice Get pool configuration * @param pool Pool address * @return config Pool configuration struct */ function getPoolConfig(address pool) external view returns (PoolConfig memory config) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); config = poolConfigs[pool]; } /** * @notice Get all registered pools * @return List of all pool addresses */ function getAllPools() external view returns (address[] memory) { return allPools; } /** * @notice Remove pool (emergency only) * @param pool Pool address to remove */ function removePool(address pool) external onlyRole(DEFAULT_ADMIN_ROLE) { require(isRegisteredPool[pool], "DODOPMMIntegration: pool not registered"); PoolConfig memory config = poolConfigs[pool]; pools[config.baseToken][config.quoteToken] = address(0); pools[config.quoteToken][config.baseToken] = address(0); isRegisteredPool[pool] = false; delete poolConfigs[pool]; emit PoolRemoved(pool); } }