// 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 "../interfaces/ICurvePool.sol"; import "../RouteTypesV2.sol"; import "../interfaces/IRouteExecutorAdapter.sol"; contract CurveRouteExecutorAdapter is IRouteExecutorAdapter { using SafeERC20 for IERC20; function validate( RouteTypesV2.RouteLeg calldata leg ) external pure override returns (bool ok, string memory reason) { if (leg.provider != RouteTypesV2.Provider.Curve) { return (false, "CurveRouteExecutorAdapter: invalid provider"); } if (leg.target == address(0)) { return (false, "CurveRouteExecutorAdapter: zero target"); } if (leg.providerData.length == 0) { return (false, "CurveRouteExecutorAdapter: missing providerData"); } return (true, ""); } function quote( RouteTypesV2.RouteLeg calldata leg, uint256 amountIn ) external view override returns (uint256 amountOut, uint256 gasEstimate) { (int128 i, int128 j,) = abi.decode(leg.providerData, (int128, int128, bool)); amountOut = ICurvePool(leg.target).get_dy(i, j, amountIn); gasEstimate = 190000; } function execute( RouteTypesV2.RouteLeg calldata leg, uint256 amountIn ) external override returns (uint256 amountOut) { (int128 i, int128 j, bool useUnderlying) = abi.decode(leg.providerData, (int128, int128, bool)); IERC20(leg.tokenIn).forceApprove(leg.target, 0); IERC20(leg.tokenIn).forceApprove(leg.target, amountIn); if (useUnderlying) { amountOut = ICurvePool(leg.target).exchange_underlying(i, j, amountIn, leg.minAmountOut); } else { amountOut = ICurvePool(leg.target).exchange(i, j, amountIn, leg.minAmountOut); } IERC20(leg.tokenOut).safeTransfer(msg.sender, amountOut); } }