Files
smom-dbis-138/contracts/bridge/trustless/EnhancedSwapRouter.sol
defiQUG 2a4753eb2d feat: restore operator WIP — PMM JSON sync entrypoint, dotenv RPC trim + secrets, pool env alignment
- Resolve stash: merge load_deployment_env path with secure-secrets and CR/LF RPC strip
- create-pmm-full-mesh-chain138.sh delegates to sync-chain138-pmm-pools-from-json.sh
- env.additions.example: canonical PMM pool defaults (cUSDT/USDT per crosscheck)
- Include Chain138 scripts, official mirror deploy scaffolding, and prior staged changes

Made-with: Cursor
2026-03-27 19:02:30 -07:00

747 lines
26 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 "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./LiquidityPoolETH.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/ICurvePool.sol";
import "./interfaces/IAggregationRouter.sol";
import "./interfaces/IDodoexRouter.sol";
import "./interfaces/IBalancerVault.sol";
import "./interfaces/IWETH.sol";
import "../../liquidity/interfaces/ILiquidityProvider.sol";
/**
* @title EnhancedSwapRouter
* @notice Multi-protocol swap router with intelligent routing and decision logic
* @dev Supports Uniswap V3, Curve, Dodoex PMM, Balancer, and 1inch aggregation
*/
contract EnhancedSwapRouter is AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant COORDINATOR_ROLE = keccak256("COORDINATOR_ROLE");
bytes32 public constant ROUTING_MANAGER_ROLE = keccak256("ROUTING_MANAGER_ROLE");
enum SwapProvider {
UniswapV3,
Curve,
Dodoex,
Balancer,
OneInch
}
// Protocol addresses
address public immutable uniswapV3Router;
address public immutable curve3Pool;
address public immutable dodoexRouter;
address public immutable balancerVault;
address public immutable oneInchRouter;
// Token addresses
address public immutable weth;
address public immutable usdt;
address public immutable usdc;
address public immutable dai;
// Routing configuration
struct RoutingConfig {
SwapProvider[] providers; // Ordered list of providers to try
uint256[] sizeThresholds; // Size thresholds in wei
bool enabled;
}
mapping(SwapProvider => bool) public providerEnabled;
mapping(uint256 => RoutingConfig) public sizeBasedRouting; // size category => config
uint256 public constant SMALL_SWAP_THRESHOLD = 10_000 * 1e18; // $10k
uint256 public constant MEDIUM_SWAP_THRESHOLD = 100_000 * 1e18; // $100k
// Uniswap V3 fee tiers
uint24 public constant FEE_TIER_LOW = 500;
uint24 public constant FEE_TIER_MEDIUM = 3000;
uint24 public constant FEE_TIER_HIGH = 10000;
// Balancer pool IDs (example - would be set via admin)
mapping(address => mapping(address => bytes32)) public balancerPoolIds; // tokenIn => tokenOut => poolId
// Dodoex PMM pool addresses (tokenIn => tokenOut => PMM pool address)
mapping(address => mapping(address => address)) public dodoPoolAddresses;
address public dodoLiquidityProvider;
/// @dev Uniswap V3 Quoter for on-chain quotes; set via setUniswapQuoter when deployed on 138/651940
address public uniswapQuoter;
event SwapExecuted(
SwapProvider indexed provider,
LiquidityPoolETH.AssetType indexed inputAsset,
address indexed tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
uint256 gasUsed
);
event RoutingConfigUpdated(uint256 sizeCategory, SwapProvider[] providers);
event ProviderToggled(SwapProvider provider, bool enabled);
error ZeroAddress();
error ZeroAmount();
error SwapFailed();
error InvalidProvider();
error ProviderDisabled();
error InsufficientOutput();
error InvalidRoutingConfig();
error DodoRouteNotConfigured();
/**
* @notice Constructor
* @param _uniswapV3Router Uniswap V3 SwapRouter address
* @param _curve3Pool Curve 3pool address
* @param _dodoexRouter Dodoex Router address
* @param _balancerVault Balancer Vault address
* @param _oneInchRouter 1inch Router address (can be address(0))
* @param _weth WETH address
* @param _usdt USDT address
* @param _usdc USDC address
* @param _dai DAI address
*/
constructor(
address _uniswapV3Router,
address _curve3Pool,
address _dodoexRouter,
address _balancerVault,
address _oneInchRouter,
address _weth,
address _usdt,
address _usdc,
address _dai
) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
if (_uniswapV3Router == address(0) || _curve3Pool == address(0) ||
_dodoexRouter == address(0) || _balancerVault == address(0) ||
_weth == address(0) || _usdt == address(0) || _usdc == address(0) || _dai == address(0)) {
revert ZeroAddress();
}
uniswapV3Router = _uniswapV3Router;
curve3Pool = _curve3Pool;
dodoexRouter = _dodoexRouter;
balancerVault = _balancerVault;
oneInchRouter = _oneInchRouter;
weth = _weth;
usdt = _usdt;
usdc = _usdc;
dai = _dai;
// Enable all providers by default
providerEnabled[SwapProvider.UniswapV3] = true;
providerEnabled[SwapProvider.Curve] = true;
providerEnabled[SwapProvider.Dodoex] = true;
providerEnabled[SwapProvider.Balancer] = true;
if (_oneInchRouter != address(0)) {
providerEnabled[SwapProvider.OneInch] = true;
}
// Initialize default routing configs
_initializeDefaultRouting();
}
/**
* @notice Swap to stablecoin using intelligent routing
* @param inputAsset Input asset type (ETH or WETH)
* @param stablecoinToken Target stablecoin
* @param amountIn Input amount
* @param amountOutMin Minimum output (slippage protection)
* @param preferredProvider Optional preferred provider (0 = auto-select)
* @return amountOut Output amount
* @return providerUsed Provider that executed the swap
*/
function swapToStablecoin(
LiquidityPoolETH.AssetType inputAsset,
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin,
SwapProvider preferredProvider
) external payable nonReentrant returns (uint256 amountOut, SwapProvider providerUsed) {
if (amountIn == 0) revert ZeroAmount();
if (stablecoinToken == address(0)) revert ZeroAddress();
if (!_isValidStablecoin(stablecoinToken)) revert("EnhancedSwapRouter: invalid stablecoin");
// Convert ETH to WETH if needed
if (inputAsset == LiquidityPoolETH.AssetType.ETH) {
require(msg.value == amountIn, "EnhancedSwapRouter: ETH amount mismatch");
IWETH(weth).deposit{value: amountIn}();
}
// Get routing providers based on swap size
SwapProvider[] memory providers = _getRoutingProviders(amountIn, preferredProvider);
// Try each provider in order
for (uint256 i = 0; i < providers.length; i++) {
if (!providerEnabled[providers[i]]) continue;
try this._executeSwap(
providers[i],
stablecoinToken,
amountIn,
amountOutMin
) returns (uint256 output) {
if (output >= amountOutMin) {
// Transfer output to caller
IERC20(stablecoinToken).safeTransfer(msg.sender, output);
emit SwapExecuted(
providers[i],
inputAsset,
weth,
stablecoinToken,
amountIn,
output,
gasleft()
);
return (output, providers[i]);
}
} catch {
// Try next provider
continue;
}
}
revert SwapFailed();
}
/**
* @notice Get quote from all enabled providers
* @param stablecoinToken Target stablecoin
* @param amountIn Input amount
* @return providers Array of providers that returned quotes
* @return amounts Array of output amounts for each provider
*/
function getQuotes(
address stablecoinToken,
uint256 amountIn
) external view returns (SwapProvider[] memory providers, uint256[] memory amounts) {
SwapProvider[] memory enabledProviders = new SwapProvider[](5);
uint256[] memory quotes = new uint256[](5);
uint256 count = 0;
// Query each enabled provider
if (providerEnabled[SwapProvider.UniswapV3]) {
try this._getUniswapV3Quote(stablecoinToken, amountIn) returns (uint256 quote) {
enabledProviders[count] = SwapProvider.UniswapV3;
quotes[count] = quote;
count++;
} catch {}
}
if (providerEnabled[SwapProvider.Dodoex]) {
try this._getDodoexQuote(stablecoinToken, amountIn) returns (uint256 quote) {
enabledProviders[count] = SwapProvider.Dodoex;
quotes[count] = quote;
count++;
} catch {}
}
if (providerEnabled[SwapProvider.Balancer]) {
try this._getBalancerQuote(stablecoinToken, amountIn) returns (uint256 quote) {
enabledProviders[count] = SwapProvider.Balancer;
quotes[count] = quote;
count++;
} catch {}
}
// Resize arrays
SwapProvider[] memory resultProviders = new SwapProvider[](count);
uint256[] memory resultQuotes = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
resultProviders[i] = enabledProviders[i];
resultQuotes[i] = quotes[i];
}
return (resultProviders, resultQuotes);
}
/**
* @notice Set routing configuration for a size category
* @param sizeCategory 0 = small, 1 = medium, 2 = large
* @param providers Ordered list of providers to try
*/
function setRoutingConfig(
uint256 sizeCategory,
SwapProvider[] calldata providers
) external onlyRole(ROUTING_MANAGER_ROLE) {
require(sizeCategory < 3, "EnhancedSwapRouter: invalid size category");
require(providers.length > 0, "EnhancedSwapRouter: empty providers");
sizeBasedRouting[sizeCategory] = RoutingConfig({
providers: providers,
sizeThresholds: new uint256[](0),
enabled: true
});
emit RoutingConfigUpdated(sizeCategory, providers);
}
/**
* @notice Toggle provider on/off
* @param provider Provider to toggle
* @param enabled Whether to enable
*/
function setProviderEnabled(
SwapProvider provider,
bool enabled
) external onlyRole(ROUTING_MANAGER_ROLE) {
providerEnabled[provider] = enabled;
emit ProviderToggled(provider, enabled);
}
/**
* @notice Set Balancer pool ID for a token pair
* @param tokenIn Input token
* @param tokenOut Output token
* @param poolId Balancer pool ID
*/
function setBalancerPoolId(
address tokenIn,
address tokenOut,
bytes32 poolId
) external onlyRole(ROUTING_MANAGER_ROLE) {
balancerPoolIds[tokenIn][tokenOut] = poolId;
}
/**
* @notice Set Dodoex PMM pool address for a token pair
* @param tokenIn Input token
* @param tokenOut Output token
* @param poolAddress Dodo PMM pool address
*/
function setDodoPoolAddress(
address tokenIn,
address tokenOut,
address poolAddress
) external onlyRole(ROUTING_MANAGER_ROLE) {
dodoPoolAddresses[tokenIn][tokenOut] = poolAddress;
}
/**
* @notice Set DODO liquidity provider implementation for environments that execute
* swaps via a local provider/integration instead of an external Dodoex router.
* @param provider Address of ILiquidityProvider-compatible contract
*/
function setDodoLiquidityProvider(address provider) external onlyRole(ROUTING_MANAGER_ROLE) {
dodoLiquidityProvider = provider;
}
/**
* @notice Set Uniswap V3 Quoter address for on-chain quotes
* @param _quoter Quoter contract address (address(0) to use 0.5% slippage estimate)
*/
function setUniswapQuoter(address _quoter) external onlyRole(ROUTING_MANAGER_ROLE) {
uniswapQuoter = _quoter;
}
/**
* @notice Swap arbitrary token pair via Dodoex when pool is configured
* @param tokenIn Input token
* @param tokenOut Output token
* @param amountIn Input amount
* @param amountOutMin Minimum output (slippage protection)
* @return amountOut Output amount
*/
function swapTokenToToken(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOutMin
) external nonReentrant returns (uint256 amountOut) {
if (amountIn == 0) revert ZeroAmount();
if (tokenIn == address(0) || tokenOut == address(0)) revert ZeroAddress();
address pool = dodoPoolAddresses[tokenIn][tokenOut];
if (!_hasDodoRoute(tokenIn, tokenOut, pool)) revert DodoRouteNotConfigured();
IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
if (dodoLiquidityProvider != address(0)) {
IERC20(tokenIn).approve(dodoLiquidityProvider, amountIn);
amountOut = ILiquidityProvider(dodoLiquidityProvider).executeSwap(
tokenIn,
tokenOut,
amountIn,
amountOutMin
);
require(amountOut >= amountOutMin, "EnhancedSwapRouter: insufficient output");
IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
return amountOut;
}
IERC20(tokenIn).approve(dodoexRouter, amountIn);
address[] memory dodoPairs = new address[](1);
dodoPairs[0] = pool;
IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
fromToken: tokenIn,
toToken: tokenOut,
fromTokenAmount: amountIn,
minReturnAmount: amountOutMin,
dodoPairs: dodoPairs,
directions: 0,
isIncentive: false,
deadLine: block.timestamp + 300
});
amountOut = IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
require(amountOut >= amountOutMin, "EnhancedSwapRouter: insufficient output");
IERC20(tokenOut).safeTransfer(msg.sender, amountOut);
return amountOut;
}
// ============ Internal Functions ============
/**
* @notice Execute swap via specified provider
*/
function _executeSwap(
SwapProvider provider,
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) external returns (uint256) {
require(msg.sender == address(this), "EnhancedSwapRouter: internal only");
if (provider == SwapProvider.UniswapV3) {
return _executeUniswapV3Swap(stablecoinToken, amountIn, amountOutMin);
} else if (provider == SwapProvider.Dodoex) {
return _executeDodoexSwap(stablecoinToken, amountIn, amountOutMin);
} else if (provider == SwapProvider.Balancer) {
return _executeBalancerSwap(stablecoinToken, amountIn, amountOutMin);
} else if (provider == SwapProvider.Curve) {
return _executeCurveSwap(stablecoinToken, amountIn, amountOutMin);
} else if (provider == SwapProvider.OneInch && oneInchRouter != address(0)) {
return _execute1inchSwap(stablecoinToken, amountIn, amountOutMin);
}
revert InvalidProvider();
}
/**
* @notice Get routing providers based on swap size
*/
function _getRoutingProviders(
uint256 amountIn,
SwapProvider preferredProvider
) internal view returns (SwapProvider[] memory) {
// If preferred provider is specified and enabled, use it first
if (preferredProvider != SwapProvider.UniswapV3 && providerEnabled[preferredProvider]) {
SwapProvider[] memory providers = new SwapProvider[](1);
providers[0] = preferredProvider;
return providers;
}
// Determine size category
uint256 category;
if (amountIn < SMALL_SWAP_THRESHOLD) {
category = 0; // Small
} else if (amountIn < MEDIUM_SWAP_THRESHOLD) {
category = 1; // Medium
} else {
category = 2; // Large
}
RoutingConfig memory config = sizeBasedRouting[category];
if (config.enabled && config.providers.length > 0) {
return config.providers;
}
// Default fallback routing
SwapProvider[] memory defaultProviders = new SwapProvider[](5);
defaultProviders[0] = SwapProvider.Dodoex;
defaultProviders[1] = SwapProvider.UniswapV3;
defaultProviders[2] = SwapProvider.Balancer;
defaultProviders[3] = SwapProvider.Curve;
defaultProviders[4] = SwapProvider.OneInch;
return defaultProviders;
}
/**
* @notice Execute Uniswap V3 swap
*/
function _executeUniswapV3Swap(
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256) {
// Approve for swap
IERC20 wethToken = IERC20(weth);
wethToken.approve(uniswapV3Router, amountIn);
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
tokenIn: weth,
tokenOut: stablecoinToken,
fee: FEE_TIER_MEDIUM,
recipient: address(this),
deadline: block.timestamp + 300,
amountIn: amountIn,
amountOutMinimum: amountOutMin,
sqrtPriceLimitX96: 0
});
return ISwapRouter(uniswapV3Router).exactInputSingle(params);
}
/**
* @notice Execute Dodoex PMM swap
*/
function _executeDodoexSwap(
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256) {
address pool = dodoPoolAddresses[weth][stablecoinToken];
if (!_hasDodoRoute(weth, stablecoinToken, pool)) revert DodoRouteNotConfigured();
if (dodoLiquidityProvider != address(0)) {
IERC20 wethTokenViaProvider = IERC20(weth);
wethTokenViaProvider.approve(dodoLiquidityProvider, amountIn);
return ILiquidityProvider(dodoLiquidityProvider).executeSwap(
weth,
stablecoinToken,
amountIn,
amountOutMin
);
}
IERC20 wethToken = IERC20(weth);
wethToken.approve(dodoexRouter, amountIn);
address[] memory dodoPairs = new address[](1);
dodoPairs[0] = pool;
IDodoexRouter.DodoSwapParams memory params = IDodoexRouter.DodoSwapParams({
fromToken: weth,
toToken: stablecoinToken,
fromTokenAmount: amountIn,
minReturnAmount: amountOutMin,
dodoPairs: dodoPairs,
directions: 0,
isIncentive: false,
deadLine: block.timestamp + 300
});
return IDodoexRouter(dodoexRouter).dodoSwapV2TokenToToken(params);
}
/**
* @notice Execute Balancer swap
*/
function _executeBalancerSwap(
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256) {
bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
require(poolId != bytes32(0), "EnhancedSwapRouter: pool not configured");
IERC20 wethToken = IERC20(weth);
wethToken.approve(balancerVault, amountIn);
IBalancerVault.SingleSwap memory singleSwap = IBalancerVault.SingleSwap({
poolId: poolId,
kind: IBalancerVault.SwapKind.GIVEN_IN,
assetIn: weth,
assetOut: stablecoinToken,
amount: amountIn,
userData: ""
});
IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
return IBalancerVault(balancerVault).swap(
singleSwap,
funds,
amountOutMin,
block.timestamp + 300
);
}
/**
* @notice Execute Curve swap
*/
function _executeCurveSwap(
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256) {
// Curve 3pool doesn't support WETH directly
// Would need intermediate swap or different pool
revert("EnhancedSwapRouter: Curve direct swap not supported");
}
/**
* @notice Execute 1inch swap
*/
function _execute1inchSwap(
address stablecoinToken,
uint256 amountIn,
uint256 amountOutMin
) internal returns (uint256) {
if (oneInchRouter == address(0)) revert ProviderDisabled();
IERC20 wethToken = IERC20(weth);
wethToken.approve(oneInchRouter, amountIn);
// 1inch: requires route data from 1inch API (e.g. /swap/v5.2/138/swap). Use 1inch SDK or API to get calldata and execute separately.
revert("EnhancedSwapRouter: 1inch requires route calldata from API; use 1inch aggregator SDK");
}
/**
* @notice Get Uniswap V3 quote (view)
* When UNISWAP_QUOTER_ADDRESS is configured, queries quoter. Otherwise returns
* estimate via amountIn * 9950/10000 (0.5% slippage) for stablecoin pairs.
*/
function _getUniswapV3Quote(
address stablecoinToken,
uint256 amountIn
) external view returns (uint256) {
if (uniswapQuoter != address(0) && _isValidStablecoin(stablecoinToken)) {
// IQuoter.quoteExactInputSingle(tokenIn, tokenOut, fee, amountIn, sqrtPriceLimitX96)
(bool ok, bytes memory data) = uniswapQuoter.staticcall(
abi.encodeWithSignature(
"quoteExactInputSingle(address,address,uint24,uint256,uint160)",
weth,
stablecoinToken,
FEE_TIER_MEDIUM,
amountIn,
uint160(0)
)
);
if (ok && data.length >= 32) {
uint256 quoted = abi.decode(data, (uint256));
if (quoted > 0) return quoted;
}
}
if (_isValidStablecoin(stablecoinToken)) {
return (amountIn * 9950) / 10000; // 0.5% slippage estimate for WETH->stable
}
return 0;
}
/**
* @notice Get Dodoex quote (view)
*/
function _getDodoexQuote(
address stablecoinToken,
uint256 amountIn
) external view returns (uint256) {
if (dodoLiquidityProvider != address(0)) {
if (!ILiquidityProvider(dodoLiquidityProvider).supportsTokenPair(weth, stablecoinToken)) {
return 0;
}
(uint256 amountOut,) = ILiquidityProvider(dodoLiquidityProvider).getQuote(weth, stablecoinToken, amountIn);
return amountOut;
}
if (dodoPoolAddresses[weth][stablecoinToken] == address(0)) {
return 0;
}
return IDodoexRouter(dodoexRouter).getDodoSwapQuote(weth, stablecoinToken, amountIn);
}
function _hasDodoRoute(
address tokenIn,
address tokenOut,
address configuredPool
) internal view returns (bool) {
if (dodoLiquidityProvider != address(0)) {
return ILiquidityProvider(dodoLiquidityProvider).supportsTokenPair(tokenIn, tokenOut);
}
return configuredPool != address(0);
}
/**
* @notice Get Balancer quote (view)
* When poolId configured, would query Balancer. Otherwise estimate for stablecoins.
*/
function _getBalancerQuote(
address stablecoinToken,
uint256 amountIn
) external view returns (uint256) {
bytes32 poolId = balancerPoolIds[weth][stablecoinToken];
if (poolId != bytes32(0)) {
(address[] memory tokens, uint256[] memory balances,) =
IBalancerVault(balancerVault).getPoolTokens(poolId);
if (tokens.length >= 2 && balances.length >= 2) {
uint256 wethIdx = type(uint256).max;
uint256 stableIdx = type(uint256).max;
for (uint256 i = 0; i < tokens.length; i++) {
if (tokens[i] == weth) wethIdx = i;
if (tokens[i] == stablecoinToken) stableIdx = i;
}
if (wethIdx != type(uint256).max && stableIdx != type(uint256).max && balances[wethIdx] > 0) {
uint256 amountOut = (amountIn * balances[stableIdx]) / balances[wethIdx];
return (amountOut * 9950) / 10000; // 0.5% slippage
}
}
}
if (_isValidStablecoin(stablecoinToken)) {
return (amountIn * 9950) / 10000; // 0.5% slippage estimate when pool not configured
}
return 0;
}
/**
* @notice Initialize default routing configurations
*/
function _initializeDefaultRouting() internal {
// Small swaps (< $10k): Uniswap V3, Dodoex
SwapProvider[] memory smallProviders = new SwapProvider[](2);
smallProviders[0] = SwapProvider.UniswapV3;
smallProviders[1] = SwapProvider.Dodoex;
sizeBasedRouting[0] = RoutingConfig({
providers: smallProviders,
sizeThresholds: new uint256[](0),
enabled: true
});
// Medium swaps ($10k-$100k): Dodoex, Balancer, Uniswap V3
SwapProvider[] memory mediumProviders = new SwapProvider[](3);
mediumProviders[0] = SwapProvider.Dodoex;
mediumProviders[1] = SwapProvider.Balancer;
mediumProviders[2] = SwapProvider.UniswapV3;
sizeBasedRouting[1] = RoutingConfig({
providers: mediumProviders,
sizeThresholds: new uint256[](0),
enabled: true
});
// Large swaps (> $100k): Dodoex, Curve, Balancer
SwapProvider[] memory largeProviders = new SwapProvider[](3);
largeProviders[0] = SwapProvider.Dodoex;
largeProviders[1] = SwapProvider.Curve;
largeProviders[2] = SwapProvider.Balancer;
sizeBasedRouting[2] = RoutingConfig({
providers: largeProviders,
sizeThresholds: new uint256[](0),
enabled: true
});
}
/**
* @notice Check if token is valid stablecoin
*/
function _isValidStablecoin(address token) internal view returns (bool) {
return token == usdt || token == usdc || token == dai;
}
// Allow contract to receive ETH
receive() external payable {}
}