93 lines
3.4 KiB
Solidity
93 lines
3.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
import {ISwapRouter} from "../bridge/trustless/interfaces/ISwapRouter.sol";
|
|
|
|
interface IDODOTwoHopSwapExactIn {
|
|
function swapExactIn(address pool, address tokenIn, uint256 amountIn, uint256 minAmountOut)
|
|
external
|
|
returns (uint256 amountOut);
|
|
}
|
|
|
|
/**
|
|
* @title TwoHopDodoToUniswapV3MultiHopExternalUnwinder
|
|
* @notice Unwinds through two DODO PMM hops followed by a final Uniswap V3 exactInput path.
|
|
* @dev `data` =
|
|
* abi.encode(
|
|
* address poolA,
|
|
* address poolB,
|
|
* address midToken,
|
|
* uint256 minMidOut,
|
|
* address intermediateToken,
|
|
* uint256 minIntermediateOut,
|
|
* bytes uniswapPath
|
|
* )
|
|
* Route shape:
|
|
* tokenIn --(poolA via DODO)--> midToken
|
|
* midToken --(poolB via DODO)--> intermediateToken
|
|
* intermediateToken --(Uniswap V3 path)--> tokenOut
|
|
*/
|
|
contract TwoHopDodoToUniswapV3MultiHopExternalUnwinder {
|
|
using SafeERC20 for IERC20;
|
|
|
|
address public immutable integration;
|
|
address public immutable router;
|
|
|
|
error BadParams();
|
|
|
|
constructor(address integration_, address router_) {
|
|
if (integration_ == address(0) || router_ == address(0)) revert BadParams();
|
|
integration = integration_;
|
|
router = router_;
|
|
}
|
|
|
|
function unwind(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes calldata data)
|
|
external
|
|
returns (uint256 amountOut)
|
|
{
|
|
if (tokenIn == address(0) || tokenOut == address(0) || tokenIn == tokenOut || amountIn == 0) revert BadParams();
|
|
|
|
(
|
|
address poolA,
|
|
address poolB,
|
|
address midToken,
|
|
uint256 minMidOut,
|
|
address intermediateToken,
|
|
uint256 minIntermediateOut,
|
|
bytes memory uniswapPath
|
|
) = abi.decode(data, (address, address, address, uint256, address, uint256, bytes));
|
|
|
|
if (poolA == address(0) || poolB == address(0) || midToken == address(0) || intermediateToken == address(0)) {
|
|
revert BadParams();
|
|
}
|
|
if (midToken == tokenIn || midToken == tokenOut || intermediateToken == tokenIn || intermediateToken == midToken) {
|
|
revert BadParams();
|
|
}
|
|
if (uniswapPath.length < 43) revert BadParams();
|
|
|
|
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
|
|
IERC20(tokenIn).forceApprove(integration, amountIn);
|
|
|
|
uint256 midOut = IDODOTwoHopSwapExactIn(integration).swapExactIn(poolA, tokenIn, amountIn, minMidOut);
|
|
if (midOut == 0) revert BadParams();
|
|
|
|
IERC20(midToken).forceApprove(integration, midOut);
|
|
uint256 intermediateOut =
|
|
IDODOTwoHopSwapExactIn(integration).swapExactIn(poolB, midToken, midOut, minIntermediateOut);
|
|
if (intermediateOut == 0) revert BadParams();
|
|
|
|
IERC20(intermediateToken).forceApprove(router, intermediateOut);
|
|
amountOut = ISwapRouter(router).exactInput(
|
|
ISwapRouter.ExactInputParams({
|
|
path: uniswapPath,
|
|
recipient: msg.sender,
|
|
deadline: block.timestamp + 300,
|
|
amountIn: intermediateOut,
|
|
amountOutMinimum: minAmountOut
|
|
})
|
|
);
|
|
}
|
|
}
|