// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../RouteTypesV2.sol"; import "../interfaces/IRouteExecutorAdapter.sol"; interface ID3ProxyView { function _DODO_APPROVE_PROXY_() external view returns (address); function sellTokens( address pool, address to, address fromToken, address toToken, uint256 fromAmount, uint256 minReceiveAmount, bytes calldata data, uint256 deadLine ) external payable returns (uint256 receiveToAmount); } interface IDODOApproveProxyView { function _DODO_APPROVE_() external view returns (address); } interface ID3MMQuoter { function querySellTokens( address fromToken, address toToken, uint256 fromAmount ) external view returns (uint256 payFromAmount, uint256 receiveToAmount, uint256 vusdAmount, uint256 swapFee, uint256 mtFee); } contract DodoV3RouteExecutorAdapter is IRouteExecutorAdapter { using SafeERC20 for IERC20; uint256 internal constant DEFAULT_GAS_ESTIMATE = 330000; function validate( RouteTypesV2.RouteLeg calldata leg ) external view override returns (bool ok, string memory reason) { if (leg.provider != RouteTypesV2.Provider.DodoV3) { return (false, "DodoV3RouteExecutorAdapter: invalid provider"); } if (leg.target == address(0)) { return (false, "DodoV3RouteExecutorAdapter: zero target"); } if (leg.tokenIn == address(0) || leg.tokenOut == address(0)) { return (false, "DodoV3RouteExecutorAdapter: zero token"); } if (leg.providerData.length != 32) { return (false, "DodoV3RouteExecutorAdapter: invalid providerData"); } address poolAddress = abi.decode(leg.providerData, (address)); if (poolAddress == address(0)) { return (false, "DodoV3RouteExecutorAdapter: zero pool"); } address approveProxy = ID3ProxyView(leg.target)._DODO_APPROVE_PROXY_(); if (approveProxy == address(0)) { return (false, "DodoV3RouteExecutorAdapter: zero approve proxy"); } address approve = IDODOApproveProxyView(approveProxy)._DODO_APPROVE_(); if (approve == address(0)) { return (false, "DodoV3RouteExecutorAdapter: zero approve"); } return (true, ""); } function quote( RouteTypesV2.RouteLeg calldata leg, uint256 amountIn ) external view override returns (uint256 amountOut, uint256 gasEstimate) { address poolAddress = abi.decode(leg.providerData, (address)); (, amountOut,,,) = ID3MMQuoter(poolAddress).querySellTokens(leg.tokenIn, leg.tokenOut, amountIn); gasEstimate = DEFAULT_GAS_ESTIMATE; } function execute( RouteTypesV2.RouteLeg calldata leg, uint256 amountIn ) external override returns (uint256 amountOut) { address poolAddress = abi.decode(leg.providerData, (address)); address approveProxy = ID3ProxyView(leg.target)._DODO_APPROVE_PROXY_(); address approve = IDODOApproveProxyView(approveProxy)._DODO_APPROVE_(); IERC20(leg.tokenIn).forceApprove(approve, 0); IERC20(leg.tokenIn).forceApprove(approve, amountIn); amountOut = ID3ProxyView(leg.target).sellTokens( poolAddress, address(this), leg.tokenIn, leg.tokenOut, amountIn, leg.minAmountOut, bytes(""), block.timestamp + 300 ); IERC20(leg.tokenIn).forceApprove(approve, 0); IERC20(leg.tokenOut).safeTransfer(msg.sender, amountOut); } }