Add DODO-only EnhancedSwapRouterV2 routing helpers
This commit is contained in:
@@ -86,10 +86,10 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
_initializeDefaultRouting();
|
||||
}
|
||||
|
||||
function setProviderAdapter(
|
||||
RouteTypesV2.Provider provider,
|
||||
address adapter
|
||||
) external onlyRole(ROUTING_MANAGER_ROLE) {
|
||||
function setProviderAdapter(RouteTypesV2.Provider provider, address adapter)
|
||||
external
|
||||
onlyRole(ROUTING_MANAGER_ROLE)
|
||||
{
|
||||
if (adapter == address(0)) revert ZeroAddress();
|
||||
providerAdapters[uint8(provider)] = adapter;
|
||||
emit ProviderAdapterSet(provider, adapter);
|
||||
@@ -103,36 +103,32 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
bytes calldata providerData,
|
||||
bool enabled
|
||||
) external onlyRole(ROUTING_MANAGER_ROLE) {
|
||||
if (tokenIn == address(0) || tokenOut == address(0) || target == address(0)) revert ZeroAddress();
|
||||
providerRoutes[tokenIn][tokenOut][uint8(provider)] = ProviderRouteConfig({
|
||||
target: target,
|
||||
providerData: providerData,
|
||||
enabled: enabled
|
||||
});
|
||||
if (tokenIn == address(0) || tokenOut == address(0) || target == address(0)) {
|
||||
revert ZeroAddress();
|
||||
}
|
||||
providerRoutes[tokenIn][tokenOut][uint8(provider)] =
|
||||
ProviderRouteConfig({target: target, providerData: providerData, enabled: enabled});
|
||||
emit ProviderRouteSet(tokenIn, tokenOut, provider, target, enabled);
|
||||
}
|
||||
|
||||
function getProviderRoute(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
RouteTypesV2.Provider provider
|
||||
) external view returns (address target, bytes memory providerData, bool enabled) {
|
||||
function getProviderRoute(address tokenIn, address tokenOut, RouteTypesV2.Provider provider)
|
||||
external
|
||||
view
|
||||
returns (address target, bytes memory providerData, bool enabled)
|
||||
{
|
||||
ProviderRouteConfig storage config = providerRoutes[tokenIn][tokenOut][uint8(provider)];
|
||||
return (config.target, config.providerData, config.enabled);
|
||||
}
|
||||
|
||||
function setProviderEnabled(
|
||||
RouteTypesV2.Provider provider,
|
||||
bool enabled
|
||||
) external onlyRole(ROUTING_MANAGER_ROLE) {
|
||||
function setProviderEnabled(RouteTypesV2.Provider provider, bool enabled) external onlyRole(ROUTING_MANAGER_ROLE) {
|
||||
providerEnabled[uint8(provider)] = enabled;
|
||||
emit ProviderToggled(provider, enabled);
|
||||
}
|
||||
|
||||
function setRoutingConfig(
|
||||
uint256 sizeCategory,
|
||||
RouteTypesV2.Provider[] calldata providers
|
||||
) external onlyRole(ROUTING_MANAGER_ROLE) {
|
||||
function setRoutingConfig(uint256 sizeCategory, RouteTypesV2.Provider[] calldata providers)
|
||||
external
|
||||
onlyRole(ROUTING_MANAGER_ROLE)
|
||||
{
|
||||
require(sizeCategory < 3, "EnhancedSwapRouterV2: invalid size category");
|
||||
require(providers.length > 0, "EnhancedSwapRouterV2: empty providers");
|
||||
|
||||
@@ -144,19 +140,23 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
emit RoutingConfigUpdated(sizeCategory, providers);
|
||||
}
|
||||
|
||||
function executeRoute(
|
||||
RouteTypesV2.RoutePlan calldata plan
|
||||
) external payable nonReentrant returns (uint256 amountOut) {
|
||||
function executeRoute(RouteTypesV2.RoutePlan calldata plan)
|
||||
external
|
||||
payable
|
||||
nonReentrant
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
RouteTypesV2.RoutePlan memory memoryPlan = _copyPlan(plan);
|
||||
return _executeRoute(memoryPlan, msg.sender, msg.value);
|
||||
}
|
||||
|
||||
function quoteConfiguredProviders(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn
|
||||
) external view returns (RouteTypesV2.ProviderQuote[] memory quotes) {
|
||||
RouteTypesV2.ProviderQuote[] memory temp = new RouteTypesV2.ProviderQuote[](uint256(type(RouteTypesV2.Provider).max) + 1);
|
||||
function quoteConfiguredProviders(address tokenIn, address tokenOut, uint256 amountIn)
|
||||
external
|
||||
view
|
||||
returns (RouteTypesV2.ProviderQuote[] memory quotes)
|
||||
{
|
||||
RouteTypesV2.ProviderQuote[] memory temp =
|
||||
new RouteTypesV2.ProviderQuote[](uint256(type(RouteTypesV2.Provider).max) + 1);
|
||||
uint256 count = 0;
|
||||
|
||||
for (uint8 providerId = 0; providerId <= uint8(type(RouteTypesV2.Provider).max); providerId++) {
|
||||
@@ -172,32 +172,13 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
if (!routeConfig.enabled || routeConfig.target == address(0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RouteTypesV2.RouteLeg memory leg = RouteTypesV2.RouteLeg({
|
||||
provider: RouteTypesV2.Provider(providerId),
|
||||
tokenIn: tokenIn,
|
||||
tokenOut: tokenOut,
|
||||
amountSource: RouteTypesV2.AmountSource.UserInput,
|
||||
minAmountOut: 0,
|
||||
target: routeConfig.target,
|
||||
providerData: routeConfig.providerData
|
||||
});
|
||||
|
||||
(bool ok,) = IRouteExecutorAdapter(adapter).validate(leg);
|
||||
if (!ok) {
|
||||
continue;
|
||||
RouteTypesV2.ProviderQuote memory quote = _quoteConfiguredProvider(
|
||||
tokenIn, tokenOut, amountIn, RouteTypesV2.Provider(providerId), routeConfig, adapter
|
||||
);
|
||||
if (quote.executable) {
|
||||
temp[count] = quote;
|
||||
count++;
|
||||
}
|
||||
|
||||
(uint256 amountOut, uint256 estimatedGas) = IRouteExecutorAdapter(adapter).quote(leg, amountIn);
|
||||
temp[count] = RouteTypesV2.ProviderQuote({
|
||||
provider: RouteTypesV2.Provider(providerId),
|
||||
target: routeConfig.target,
|
||||
amountOut: amountOut,
|
||||
estimatedGas: estimatedGas,
|
||||
executable: amountOut > 0,
|
||||
providerData: routeConfig.providerData
|
||||
});
|
||||
count++;
|
||||
}
|
||||
|
||||
quotes = new RouteTypesV2.ProviderQuote[](count);
|
||||
@@ -206,12 +187,69 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
}
|
||||
}
|
||||
|
||||
function swapTokenToToken(
|
||||
function quoteConfiguredProvider(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin
|
||||
) external returns (uint256 amountOut) {
|
||||
RouteTypesV2.Provider provider
|
||||
) external view returns (RouteTypesV2.ProviderQuote memory quote) {
|
||||
uint8 providerId = uint8(provider);
|
||||
if (!providerEnabled[providerId]) revert ProviderDisabled();
|
||||
|
||||
address adapter = providerAdapters[providerId];
|
||||
if (adapter == address(0)) revert ProviderAdapterNotConfigured();
|
||||
|
||||
ProviderRouteConfig storage routeConfig = providerRoutes[tokenIn][tokenOut][providerId];
|
||||
if (!routeConfig.enabled || routeConfig.target == address(0)) revert RouteNotConfigured();
|
||||
|
||||
return _quoteConfiguredProvider(tokenIn, tokenOut, amountIn, provider, routeConfig, adapter);
|
||||
}
|
||||
|
||||
function _quoteConfiguredProvider(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn,
|
||||
RouteTypesV2.Provider provider,
|
||||
ProviderRouteConfig storage routeConfig,
|
||||
address adapter
|
||||
) internal view returns (RouteTypesV2.ProviderQuote memory quote) {
|
||||
RouteTypesV2.RouteLeg memory leg = RouteTypesV2.RouteLeg({
|
||||
provider: provider,
|
||||
tokenIn: tokenIn,
|
||||
tokenOut: tokenOut,
|
||||
amountSource: RouteTypesV2.AmountSource.UserInput,
|
||||
minAmountOut: 0,
|
||||
target: routeConfig.target,
|
||||
providerData: routeConfig.providerData
|
||||
});
|
||||
|
||||
(bool ok,) = IRouteExecutorAdapter(adapter).validate(leg);
|
||||
if (!ok) {
|
||||
return RouteTypesV2.ProviderQuote({
|
||||
provider: provider,
|
||||
target: routeConfig.target,
|
||||
amountOut: 0,
|
||||
estimatedGas: 0,
|
||||
executable: false,
|
||||
providerData: routeConfig.providerData
|
||||
});
|
||||
}
|
||||
|
||||
(uint256 amountOut, uint256 estimatedGas) = IRouteExecutorAdapter(adapter).quote(leg, amountIn);
|
||||
return RouteTypesV2.ProviderQuote({
|
||||
provider: provider,
|
||||
target: routeConfig.target,
|
||||
amountOut: amountOut,
|
||||
estimatedGas: estimatedGas,
|
||||
executable: amountOut > 0,
|
||||
providerData: routeConfig.providerData
|
||||
});
|
||||
}
|
||||
|
||||
function swapTokenToToken(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin)
|
||||
external
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
return swapTokenToToken(tokenIn, tokenOut, amountIn, amountOutMin, _defaultPreferredProvider());
|
||||
}
|
||||
|
||||
@@ -295,11 +333,10 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
return _executeRoute(plan, msg.sender, msg.value);
|
||||
}
|
||||
|
||||
function _executeRoute(
|
||||
RouteTypesV2.RoutePlan memory plan,
|
||||
address payer,
|
||||
uint256 nativeValue
|
||||
) internal returns (uint256 amountOut) {
|
||||
function _executeRoute(RouteTypesV2.RoutePlan memory plan, address payer, uint256 nativeValue)
|
||||
internal
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
_validatePlan(plan);
|
||||
|
||||
address currentToken = plan.inputToken;
|
||||
@@ -360,29 +397,29 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
return currentAmount;
|
||||
}
|
||||
|
||||
function _getRouteConfig(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
RouteTypesV2.Provider preferredProvider
|
||||
) internal view returns (ProviderRouteConfig memory) {
|
||||
function _getRouteConfig(address tokenIn, address tokenOut, RouteTypesV2.Provider preferredProvider)
|
||||
internal
|
||||
view
|
||||
returns (ProviderRouteConfig memory)
|
||||
{
|
||||
RouteTypesV2.Provider[] memory providers = _getRoutingProviders(preferredProvider);
|
||||
for (uint256 i = 0; i < providers.length; i++) {
|
||||
uint8 providerId = uint8(providers[i]);
|
||||
ProviderRouteConfig storage config = providerRoutes[tokenIn][tokenOut][providerId];
|
||||
if (config.enabled && config.target != address(0) && providerEnabled[providerId]) {
|
||||
return ProviderRouteConfig({
|
||||
target: config.target,
|
||||
providerData: config.providerData,
|
||||
enabled: config.enabled
|
||||
target: config.target, providerData: config.providerData, enabled: config.enabled
|
||||
});
|
||||
}
|
||||
}
|
||||
revert RouteNotConfigured();
|
||||
}
|
||||
|
||||
function _getRoutingProviders(
|
||||
RouteTypesV2.Provider preferredProvider
|
||||
) internal view returns (RouteTypesV2.Provider[] memory providers) {
|
||||
function _getRoutingProviders(RouteTypesV2.Provider preferredProvider)
|
||||
internal
|
||||
view
|
||||
returns (RouteTypesV2.Provider[] memory providers)
|
||||
{
|
||||
if (providerEnabled[uint8(preferredProvider)]) {
|
||||
providers = new RouteTypesV2.Provider[](1);
|
||||
providers[0] = preferredProvider;
|
||||
@@ -420,9 +457,11 @@ contract EnhancedSwapRouterV2 is AccessControl, ReentrancyGuard {
|
||||
sizeBasedRouting[2].push(RouteTypesV2.Provider.Balancer);
|
||||
}
|
||||
|
||||
function _copyPlan(
|
||||
RouteTypesV2.RoutePlan calldata plan
|
||||
) internal pure returns (RouteTypesV2.RoutePlan memory copied) {
|
||||
function _copyPlan(RouteTypesV2.RoutePlan calldata plan)
|
||||
internal
|
||||
pure
|
||||
returns (RouteTypesV2.RoutePlan memory copied)
|
||||
{
|
||||
copied.chainId = plan.chainId;
|
||||
copied.inputToken = plan.inputToken;
|
||||
copied.outputToken = plan.outputToken;
|
||||
|
||||
@@ -5,22 +5,23 @@ import "../RouteTypesV2.sol";
|
||||
import "../LiquidityPoolETH.sol";
|
||||
|
||||
interface IEnhancedSwapRouterV2 {
|
||||
function executeRoute(
|
||||
RouteTypesV2.RoutePlan calldata plan
|
||||
) external payable returns (uint256 amountOut);
|
||||
function executeRoute(RouteTypesV2.RoutePlan calldata plan) external payable returns (uint256 amountOut);
|
||||
|
||||
function quoteConfiguredProviders(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn
|
||||
) external view returns (RouteTypesV2.ProviderQuote[] memory quotes);
|
||||
function quoteConfiguredProviders(address tokenIn, address tokenOut, uint256 amountIn)
|
||||
external
|
||||
view
|
||||
returns (RouteTypesV2.ProviderQuote[] memory quotes);
|
||||
|
||||
function swapTokenToToken(
|
||||
function quoteConfiguredProvider(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin
|
||||
) external returns (uint256 amountOut);
|
||||
RouteTypesV2.Provider provider
|
||||
) external view returns (RouteTypesV2.ProviderQuote memory quote);
|
||||
|
||||
function swapTokenToToken(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin)
|
||||
external
|
||||
returns (uint256 amountOut);
|
||||
|
||||
function swapToStablecoin(
|
||||
LiquidityPoolETH.AssetType inputAsset,
|
||||
|
||||
122
contracts/liquidity/providers/FixedRateLiquidityProvider.sol
Normal file
122
contracts/liquidity/providers/FixedRateLiquidityProvider.sol
Normal file
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/access/AccessControl.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "../interfaces/ILiquidityProvider.sol";
|
||||
|
||||
/**
|
||||
* @title FixedRateLiquidityProvider
|
||||
* @notice Narrow Chain 138 prep provider for token pairs that need executable
|
||||
* router-backed conversion while a DODO pool direction is unhealthy.
|
||||
*/
|
||||
contract FixedRateLiquidityProvider is ILiquidityProvider, AccessControl {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
bytes32 public constant RATE_MANAGER_ROLE = keccak256("RATE_MANAGER_ROLE");
|
||||
|
||||
address public immutable tokenIn;
|
||||
address public immutable tokenOut;
|
||||
|
||||
uint256 public amountOutPerTokenIn;
|
||||
uint256 public slippageBps;
|
||||
uint256 public gasEstimate;
|
||||
bool public enabled = true;
|
||||
|
||||
event RateUpdated(uint256 amountOutPerTokenIn, uint256 slippageBps);
|
||||
event EnabledSet(bool enabled);
|
||||
event SwapExecuted(address indexed caller, uint256 amountIn, uint256 amountOut);
|
||||
event Swept(address indexed token, address indexed recipient, uint256 amount);
|
||||
|
||||
constructor(
|
||||
address admin,
|
||||
address tokenIn_,
|
||||
address tokenOut_,
|
||||
uint256 amountOutPerTokenIn_,
|
||||
uint256 slippageBps_,
|
||||
uint256 gasEstimate_
|
||||
) {
|
||||
require(admin != address(0), "FixedRateLiquidityProvider: zero admin");
|
||||
require(tokenIn_ != address(0), "FixedRateLiquidityProvider: zero tokenIn");
|
||||
require(tokenOut_ != address(0), "FixedRateLiquidityProvider: zero tokenOut");
|
||||
require(tokenIn_ != tokenOut_, "FixedRateLiquidityProvider: same token");
|
||||
require(amountOutPerTokenIn_ > 0, "FixedRateLiquidityProvider: zero rate");
|
||||
|
||||
tokenIn = tokenIn_;
|
||||
tokenOut = tokenOut_;
|
||||
amountOutPerTokenIn = amountOutPerTokenIn_;
|
||||
slippageBps = slippageBps_;
|
||||
gasEstimate = gasEstimate_;
|
||||
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
||||
_grantRole(RATE_MANAGER_ROLE, admin);
|
||||
}
|
||||
|
||||
function setRate(uint256 amountOutPerTokenIn_, uint256 slippageBps_) external onlyRole(RATE_MANAGER_ROLE) {
|
||||
require(amountOutPerTokenIn_ > 0, "FixedRateLiquidityProvider: zero rate");
|
||||
amountOutPerTokenIn = amountOutPerTokenIn_;
|
||||
slippageBps = slippageBps_;
|
||||
emit RateUpdated(amountOutPerTokenIn_, slippageBps_);
|
||||
}
|
||||
|
||||
function setEnabled(bool enabled_) external onlyRole(RATE_MANAGER_ROLE) {
|
||||
enabled = enabled_;
|
||||
emit EnabledSet(enabled_);
|
||||
}
|
||||
|
||||
function setGasEstimate(uint256 gasEstimate_) external onlyRole(RATE_MANAGER_ROLE) {
|
||||
gasEstimate = gasEstimate_;
|
||||
}
|
||||
|
||||
function getQuote(
|
||||
address tokenIn_,
|
||||
address tokenOut_,
|
||||
uint256 amountIn
|
||||
) external view override returns (uint256 amountOut, uint256 quoteSlippageBps) {
|
||||
if (!enabled || tokenIn_ != tokenIn || tokenOut_ != tokenOut || amountIn == 0) {
|
||||
return (0, 10_000);
|
||||
}
|
||||
|
||||
amountOut = (amountIn * amountOutPerTokenIn) / 1e18;
|
||||
quoteSlippageBps = slippageBps;
|
||||
}
|
||||
|
||||
function executeSwap(
|
||||
address tokenIn_,
|
||||
address tokenOut_,
|
||||
uint256 amountIn,
|
||||
uint256 minAmountOut
|
||||
) external override returns (uint256 amountOut) {
|
||||
require(enabled, "FixedRateLiquidityProvider: disabled");
|
||||
require(tokenIn_ == tokenIn && tokenOut_ == tokenOut, "FixedRateLiquidityProvider: unsupported pair");
|
||||
require(amountIn > 0, "FixedRateLiquidityProvider: zero amount");
|
||||
|
||||
amountOut = (amountIn * amountOutPerTokenIn) / 1e18;
|
||||
require(amountOut >= minAmountOut, "FixedRateLiquidityProvider: insufficient output");
|
||||
require(IERC20(tokenOut).balanceOf(address(this)) >= amountOut, "FixedRateLiquidityProvider: insufficient liquidity");
|
||||
|
||||
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
|
||||
IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
|
||||
|
||||
emit SwapExecuted(msg.sender, amountIn, amountOut);
|
||||
}
|
||||
|
||||
function supportsTokenPair(address tokenIn_, address tokenOut_) external view override returns (bool) {
|
||||
return enabled && tokenIn_ == tokenIn && tokenOut_ == tokenOut;
|
||||
}
|
||||
|
||||
function providerName() external pure override returns (string memory) {
|
||||
return "Fixed Rate Liquidity Provider";
|
||||
}
|
||||
|
||||
function estimateGas(address, address, uint256) external view override returns (uint256) {
|
||||
return gasEstimate;
|
||||
}
|
||||
|
||||
function sweep(address token, address recipient, uint256 amount) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
||||
require(recipient != address(0), "FixedRateLiquidityProvider: zero recipient");
|
||||
IERC20(token).safeTransfer(recipient, amount);
|
||||
emit Swept(token, recipient, amount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user