- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
109 lines
3.7 KiB
Solidity
109 lines
3.7 KiB
Solidity
// 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);
|
|
}
|
|
}
|