Add DODO-only EnhancedSwapRouterV2 routing helpers
This commit is contained in:
@@ -18,14 +18,14 @@
|
||||
"UNIVERSAL_ASSET_REGISTRY": "0xAEE4b7fBe82E1F8295951584CBc772b8BBD68575",
|
||||
"GOVERNANCE_CONTROLLER": "0xA6891D5229f2181a34D4FF1B515c3Aa37dd90E0e",
|
||||
"BRIDGE_ORCHESTRATOR": "0x89aB428c437f23bAB9781ff8Db8D3848e27EeD6c",
|
||||
"ENHANCED_SWAP_ROUTER_V2_ADDRESS": "0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce",
|
||||
"INTENT_BRIDGE_COORDINATOR_V2_ADDRESS": "0x7D0022B7e8360172fd9C0bB6778113b7Ea3674E7",
|
||||
"DODO_ROUTE_EXECUTOR_ADAPTER": "0x88495B3dccEA93b0633390fDE71992683121Fa62",
|
||||
"DODO_V3_ROUTE_EXECUTOR_ADAPTER": "0x9Cb97adD29c52e3B81989BcA2E33D46074B530eF",
|
||||
"UNISWAP_V3_ROUTE_EXECUTOR_ADAPTER": "0x960D6db4E78705f82995690548556fb2266308EA",
|
||||
"BALANCER_ROUTE_EXECUTOR_ADAPTER": "0x4E1B71B69188Ab45021c797039b4887a4924157A",
|
||||
"CURVE_ROUTE_EXECUTOR_ADAPTER": "0x5f0E07071c41ACcD2A1b1032D3bd49b323b9ADE6",
|
||||
"ONEINCH_ROUTE_EXECUTOR_ADAPTER": "0x8168083d29b3293F215392A49D16e7FeF4a02600",
|
||||
"ENHANCED_SWAP_ROUTER_V2_ADDRESS": "0xa421706768aeb7fafa2d912c5e10824ef3437ad4",
|
||||
"INTENT_BRIDGE_COORDINATOR_V2_ADDRESS": "0x7aa03fa96e884ec503510d725e68301ff9fe53c1",
|
||||
"DODO_ROUTE_EXECUTOR_ADAPTER": "0xa505eaee26c6503c63eaa6b4b05a28bb40097f25",
|
||||
"DODO_V3_ROUTE_EXECUTOR_ADAPTER": "0x046ce4030af57b3057c052a48ebaacfac74ec1b2",
|
||||
"UNISWAP_V3_ROUTE_EXECUTOR_ADAPTER": "0x51f6fc9b76f2b319837118d93daeb96fae09da56",
|
||||
"BALANCER_ROUTE_EXECUTOR_ADAPTER": "0xb36b60a16f8ac53379c7547841cb74cbc1d490d5",
|
||||
"CURVE_ROUTE_EXECUTOR_ADAPTER": "0xd1af10f0d2d5fafc55dee47560e985afa1ee7f90",
|
||||
"ONEINCH_ROUTE_EXECUTOR_ADAPTER": "0x0e1b66e7997dcef2e271fc28eb8a38520eb7aad0",
|
||||
"UNISWAP_V3_ROUTER": "0xde9cD8ee2811E6E64a41D5F68Be315d33995975E",
|
||||
"UNISWAP_QUOTER_ADDRESS": "0x6abbB1CEb2468e748a03A00CD6aA9BFE893AFa1f",
|
||||
"CHAIN_138_UNISWAP_V3_FACTORY": "0x2f7219276e3ce367dB9ec74C1196a8ecEe67841C",
|
||||
@@ -95,6 +95,7 @@
|
||||
"POOL_CUSDC_XAU_PUBLIC": "0xEa9AC6357CaCB42a83b9082B870610363b177CbA",
|
||||
"POOL_CEURT_XAU_PUBLIC": "0xba99bc1eAac164569d5aca96c806934dDaf970CF",
|
||||
"CHAIN138_POOL_WETH_USDT": "0xe227f6c0520c0c6e8786fe56fa76c4914f861533",
|
||||
"CHAIN138_POOL_WETH_USDC": "0xb53a0508940b1ff90f1aad4f6cb50a7012fe5593"
|
||||
"CHAIN138_POOL_WETH_USDC": "0xb53a0508940b1ff90f1aad4f6cb50a7012fe5593",
|
||||
"POOL_WETH_CUSDC": "0xaae68830a55767722618e869882c6ed064cc1eb2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"CCADC_ADDRESS_138": "0x54dBd40cF05e15906A2C21f600937e96787f5679",
|
||||
"CXAUC_ADDRESS_138": "0x290E52a8819A4fbD0714E517225429aA2B70EC6b",
|
||||
"CXAUT_ADDRESS_138": "0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E",
|
||||
"ENHANCED_SWAP_ROUTER_V2_ADDRESS": "0xa421706768aeb7fafa2d912c5e10824ef3437ad4",
|
||||
"ENHANCED_SWAP_ROUTER_CHAIN138": "0xE6Cc7643ae2A4C720A28D8263BC4972905d7DE0f",
|
||||
"ENHANCED_SWAP_ROUTER_ADDRESS": "0xE6Cc7643ae2A4C720A28D8263BC4972905d7DE0f",
|
||||
"ENHANCED_SWAP_ROUTER_V1_ADDRESS": "0xE6Cc7643ae2A4C720A28D8263BC4972905d7DE0f"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# Chain 138 Atomic Swap WETH -> cUSDC Prep Route
|
||||
|
||||
Date: 2026-04-29
|
||||
|
||||
## Scope
|
||||
|
||||
Published the executable Chain 138 prep lane required for Atomic Swap to route native ETH into the live cUSDC -> cWUSDC GRU bridge lane:
|
||||
|
||||
1. Wrap native ETH into WETH.
|
||||
2. Swap WETH -> cUSDC on Chain 138 through `EnhancedSwapRouterV2`.
|
||||
3. Bridge cUSDC on Chain 138 -> cWUSDC on Ethereum Mainnet.
|
||||
|
||||
The earlier DODO PMM route quoted successfully but reverted during execution for WETH input. This report records the replacement route that was validated by simulating `EnhancedSwapRouterV2.swapTokenToToken`.
|
||||
|
||||
## Live Router Configuration
|
||||
|
||||
- EnhancedSwapRouterV2: `0xa421706768aeb7fafa2d912c5e10824ef3437ad4`
|
||||
- FixedRateLiquidityProvider: `0x16E07328C27f5adc6BCA1DefD6B3957E89Ca0eB2`
|
||||
- Token in: WETH `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2`
|
||||
- Token out: cUSDC `0xf22258f57794CC8E06237084b353Ab30fFfa640b`
|
||||
- Rate: `2277318023` raw cUSDC per `1e18` WETH
|
||||
- Provider cUSDC funding: `20,000,000` cUSDC
|
||||
|
||||
Transactions:
|
||||
|
||||
- Provider deployment: `0xf8b6738e10318055025bc93079eb2902c37c1462f16f4907c92ce00b883f16bc`
|
||||
- Provider cUSDC funding: `0xc44ef055a53e02ae3b701b397eb312fbe3b861c964457d749c8106bbf2f374c1`
|
||||
- Router WETH -> cUSDC provider route: `0x16600cda551ae505acaf9491edbc2515806cfe20daa7e643b258577d05d06122`
|
||||
|
||||
## Verification
|
||||
|
||||
- `quoteConfiguredProvider(WETH, cUSDC, 2500e18, Dodo)` returns `5,693,295.0575` cUSDC and executable `true`.
|
||||
- `swapTokenToToken(WETH, cUSDC, 2500e18, 5635168323541)` simulates successfully from the deployer and returns `5,693,295.0575` cUSDC.
|
||||
- `swapTokenToToken(WETH, cUSDC, 300e18, 1)` simulates successfully and returns `683,195.4069` cUSDC.
|
||||
- Atomic Swap public registry includes `chain-138-swap-enhanced-router-v2-weth-cusdc-0x16e07328`.
|
||||
- Atomic Swap live browser check for `300 ETH` on Chain 138 -> `cWUSDC` on Ethereum Mainnet shows `Canonical bridge lane` and `Bridge ready`.
|
||||
- Ethereum Mainnet cWUSDC bridge `0x2bF74583206A49Be07E0E8A94197C12987AbD7B5` has the cWUSDC `MINTER_ROLE`.
|
||||
- `ccip-relay.service` is active on `192.168.11.11`.
|
||||
- `npm run validate:manifest` passed.
|
||||
- `npm run build` passed.
|
||||
- `npm run audit:live` passed for `https://atomic-swap.defi-oracle.io/`.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Chain 138 EnhancedSwapRouterV2 DODO-Only Routing Broadcast
|
||||
|
||||
Date: 2026-04-29 20:22 PDT
|
||||
|
||||
Router: `0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce`
|
||||
|
||||
Signer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8`
|
||||
|
||||
Purpose: set the `EnhancedSwapRouterV2` size-based fallback routing lists to DODO-only for size categories `0`, `1`, and `2`, then disable non-DODO provider flags. This preserves exact DODO-family configured routes while avoiding mixed-provider fallback discovery for DODO-backed pairs.
|
||||
|
||||
The three calls were simulated with `cast call` before broadcast. All simulations returned `0x`.
|
||||
|
||||
| Size category | Call | Tx hash | Block | Status | Gas used |
|
||||
|---:|---|---|---:|---|---:|
|
||||
| 0 | `setRoutingConfig(0, [Dodo])` | `0xbb13f084f401aad0ffe57e81e541fd6596b71402db46ee19ee465966471156f7` | 4562687 | success | 32944 |
|
||||
| 1 | `setRoutingConfig(1, [Dodo])` | `0xb6eb6aa76ded96b7105fbd99be8720be15a426c094d70b1cf8c7e53de173af27` | 4562688 | success | 32956 |
|
||||
| 2 | `setRoutingConfig(2, [Dodo])` | `0x94cd1d180334041d11dbc4187b8a54f72b7957b26bc5b1c2b0a4c2325df8c53e` | 4562689 | success | 32956 |
|
||||
|
||||
Receipt evidence: each transaction emitted `RoutingConfigUpdated(uint256,Provider[])` with the size category as the indexed topic and one provider element `0` (`RouteTypesV2.Provider.Dodo`) in event data.
|
||||
|
||||
Additional provider toggles were broadcast after a read check showed non-DODO providers had been re-enabled by earlier pilot wiring.
|
||||
|
||||
| Provider | Call | Tx hash | Block | Status | Gas used |
|
||||
|---:|---|---|---:|---|---:|
|
||||
| 1 | `setProviderEnabled(UniswapV3, false)` | `0x8795c878dbe667ca35c2dab603bc7ae9f0de3c44958a14c0f8a573db822531a0` | 4562720 | success | 25845 |
|
||||
| 2 | `setProviderEnabled(Balancer, false)` | `0xc1eb8c77a0a8bf89df762931a8f5dd69e7ef1344af60b71e2fc7cdcbb3c62c62` | 4562721 | success | 25845 |
|
||||
| 3 | `setProviderEnabled(Curve, false)` | `0x4615b6a9345e78cc4adfdac494eafa834437e87963d97038caa6d86f64c2ffd2` | 4562722 | success | 25845 |
|
||||
| 4 | `setProviderEnabled(OneInch, false)` | `0xd0dceb53c470adf81cbea5673836b4d1a034da778545039ed10431bf86c4ae32` | 4562723 | success | 25845 |
|
||||
| 5 | `setProviderEnabled(Partner, false)` | `0x24ccfad90426ab33981ebdddbd826eca79b99301a6c792b5946c10f53f168f96` | 4562724 | success | 27845 |
|
||||
|
||||
Final provider flag check:
|
||||
|
||||
| Provider ID | Provider | Enabled |
|
||||
|---:|---|---|
|
||||
| 0 | Dodo | `true` |
|
||||
| 1 | UniswapV3 | `false` |
|
||||
| 2 | Balancer | `false` |
|
||||
| 3 | Curve | `false` |
|
||||
| 4 | OneInch | `false` |
|
||||
| 5 | Partner | `false` |
|
||||
| 6 | DodoV3 | `true` |
|
||||
|
||||
Functional read check: `quoteConfiguredProviders(cUSDT, cUSDC, 1e18)` returned one DODO provider quote candidate only.
|
||||
|
||||
Note: these were broadcast with direct `cast send`, not `forge script --broadcast`, so there is no Foundry broadcast JSON artifact for these calls.
|
||||
@@ -0,0 +1,66 @@
|
||||
# Chain 138 EnhancedSwapRouterV2 Redeploy And Cutover
|
||||
|
||||
Date: 2026-04-29 PDT
|
||||
|
||||
Signer: `0x4A666F96fC8764181194447A7dFdb7d471b301C8`
|
||||
|
||||
Purpose: redeploy `EnhancedSwapRouterV2` with the new `quoteConfiguredProvider(...)` single-provider quote path, then cut the Chain 138 V2 router inventory to the new address.
|
||||
|
||||
## Deployed Contracts
|
||||
|
||||
| Contract | Address | Tx hash | Block |
|
||||
|---|---|---|---:|
|
||||
| DODO RouteExecutorAdapter | `0xa505eaee26c6503c63eaa6b4b05a28bb40097f25` | `0xa1aeada5d6e1b351513a72c133e22d5dce8c6b3f1b55723b3d0ab07cb534e55f` | 4563327 |
|
||||
| DODO V3 RouteExecutorAdapter | `0x046ce4030af57b3057c052a48ebaacfac74ec1b2` | `0x9f6f126805170d50ab57c33f5afeb66a6b965d00a564a99cf842e0e7d2aa43d6` | 4563328 |
|
||||
| Uniswap V3 RouteExecutorAdapter | `0x51f6fc9b76f2b319837118d93daeb96fae09da56` | `0xccfed88aa3b1cb555be27292b067de4f8871b4fdb77158430800d78e2a677ad6` | 4563329 |
|
||||
| Balancer RouteExecutorAdapter | `0xb36b60a16f8ac53379c7547841cb74cbc1d490d5` | `0x923e771d70e94ee8cb203f4a105141cdf5763b2c02bf36f1cfbd950dd4bba8cf` | 4563330 |
|
||||
| Curve RouteExecutorAdapter | `0xd1af10f0d2d5fafc55dee47560e985afa1ee7f90` | `0xeb004c4b4bb5741ec077bb23f30bdedc60e00f562cd222a0de997696aaffe430` | 4563331 |
|
||||
| 1inch RouteExecutorAdapter | `0x0e1b66e7997dcef2e271fc28eb8a38520eb7aad0` | `0xb3e05ead43dd640db296d7fddcb72289deba6dc771dfb45bd98228c1a89d9cf7` | 4563332 |
|
||||
| EnhancedSwapRouterV2 | `0xa421706768aeb7fafa2d912c5e10824ef3437ad4` | `0xe3430bcfcf7809ca6574f9ae22d243f5945b2e2ee186c4da5f25f1cdd4c843b4` | 4563333 |
|
||||
| IntentBridgeCoordinatorV2 | `0x7aa03fa96e884ec503510d725e68301ff9fe53c1` | `0x9a7c24aa58dbe303ec9ee4e98ca67eb23af894de7ab704de9a52d6535d7ad759` | 4563334 |
|
||||
|
||||
## Router Configuration
|
||||
|
||||
The new router was configured with:
|
||||
|
||||
- Adapter mappings for DODO, DODO V3, Uniswap V3, Balancer, Curve, and 1inch.
|
||||
- DODO-only size routing:
|
||||
- `setRoutingConfig(0, [Dodo])`: `0x226eda46936e8d1c89208ae45ca5777dd224a6404eb7de1b5e7a8e614b0a9934`
|
||||
- `setRoutingConfig(1, [Dodo])`: `0xda55d6139c1862870ac502f6c14fa6001ea65bf8279a9dfbd3a19cca82699e3a`
|
||||
- `setRoutingConfig(2, [Dodo])`: `0x4780de510031c9f66d5be3630391a253f3650dc089e0b54fdd28d293fbb23a74`
|
||||
- Bidirectional DODO provider routes for cUSDT/cUSDC, cUSDT/USDT, cUSDC/USDC, cUSDT/cXAUC, cUSDC/cXAUC, and cEURT/cXAUC.
|
||||
- Bidirectional DODO V3 provider route for WETH10/USDT.
|
||||
|
||||
Final provider flag check:
|
||||
|
||||
| Provider ID | Provider | Enabled |
|
||||
|---:|---|---|
|
||||
| 0 | Dodo | `true` |
|
||||
| 1 | UniswapV3 | `false` |
|
||||
| 2 | Balancer | `false` |
|
||||
| 3 | Curve | `false` |
|
||||
| 4 | OneInch | `false` |
|
||||
| 5 | Partner | `false` |
|
||||
| 6 | DodoV3 | `true` |
|
||||
|
||||
Non-DODO disable txs:
|
||||
|
||||
- UniswapV3: `0xbb17e5193c2d3043a42d8afdf18bef1bd6470e72420b20d2f3693e4a2e09d200`
|
||||
- Balancer: `0x2e630057e944d5dbe863244a9e0500f99ad64d8a620cd5f02b0a2093e5629af9`
|
||||
- Curve: `0x6966e8ef4961eae4d5d56ec021a512a72bbb56379b4af98f12a8ab54fba34655`
|
||||
|
||||
## Cutover Files
|
||||
|
||||
Updated:
|
||||
|
||||
- `config/address-inventory.chain138.json`
|
||||
- `config/runtime-env.chain138.json`
|
||||
- `.env`
|
||||
- `reports/inventory/DEPLOYED_CONTRACTS_UNIFIED_EXTENDED.md`
|
||||
- `reports/inventory/deployed-contracts-by-network.md`
|
||||
|
||||
## Verification
|
||||
|
||||
- `cast code` confirmed bytecode at the new router and coordinator.
|
||||
- `quoteConfiguredProvider(cUSDT, cUSDC, 1e18, Dodo)` returned successfully from the new router selector.
|
||||
- `getProviderRoute(cUSDT, cUSDC, Dodo)` returned the DODO provider target and configured pool bytes.
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Script, console} from "forge-std/Script.sol";
|
||||
import "../../../contracts/bridge/trustless/EnhancedSwapRouterV2.sol";
|
||||
import "../../../contracts/bridge/trustless/RouteTypesV2.sol";
|
||||
|
||||
contract ConfigureEnhancedSwapRouterV2DodoOnly is Script {
|
||||
address constant LIVE_ROUTER_V2 = 0xF1c93F54A5C2fc0d7766Ccb0Ad8f157DFB4C99Ce;
|
||||
|
||||
function run() external {
|
||||
require(block.chainid == 138, "ConfigureEnhancedSwapRouterV2DodoOnly: Chain 138 only");
|
||||
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
address routerAddress = vm.envOr("ENHANCED_SWAP_ROUTER_V2_ADDRESS", LIVE_ROUTER_V2);
|
||||
|
||||
EnhancedSwapRouterV2 router = EnhancedSwapRouterV2(payable(routerAddress));
|
||||
RouteTypesV2.Provider[] memory providers = new RouteTypesV2.Provider[](1);
|
||||
providers[0] = RouteTypesV2.Provider.Dodo;
|
||||
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
router.setRoutingConfig(0, providers);
|
||||
router.setRoutingConfig(1, providers);
|
||||
router.setRoutingConfig(2, providers);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Curve, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Partner, false);
|
||||
vm.stopBroadcast();
|
||||
|
||||
console.log("EnhancedSwapRouterV2 DODO-only routing configured:", routerAddress);
|
||||
console.log("sizeCategory 0 provider:", uint8(providers[0]));
|
||||
console.log("sizeCategory 1 provider:", uint8(providers[0]));
|
||||
console.log("sizeCategory 2 provider:", uint8(providers[0]));
|
||||
console.log("disabled provider:", uint8(RouteTypesV2.Provider.UniswapV3));
|
||||
console.log("disabled provider:", uint8(RouteTypesV2.Provider.Balancer));
|
||||
console.log("disabled provider:", uint8(RouteTypesV2.Provider.Curve));
|
||||
console.log("disabled provider:", uint8(RouteTypesV2.Provider.OneInch));
|
||||
console.log("disabled provider:", uint8(RouteTypesV2.Provider.Partner));
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,8 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
CurveRouteExecutorAdapter curveAdapter = new CurveRouteExecutorAdapter();
|
||||
OneInchRouteExecutorAdapter oneInchAdapter = new OneInchRouteExecutorAdapter();
|
||||
|
||||
EnhancedSwapRouterV2 router = new EnhancedSwapRouterV2(
|
||||
CHAIN138_WETH,
|
||||
CHAIN138_USDT,
|
||||
CHAIN138_USDC,
|
||||
CHAIN138_DAI_PLACEHOLDER
|
||||
);
|
||||
EnhancedSwapRouterV2 router =
|
||||
new EnhancedSwapRouterV2(CHAIN138_WETH, CHAIN138_USDT, CHAIN138_USDC, CHAIN138_DAI_PLACEHOLDER);
|
||||
IntentBridgeCoordinatorV2 coordinator = new IntentBridgeCoordinatorV2(address(router));
|
||||
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Dodo, address(dodoAdapter));
|
||||
@@ -94,6 +90,7 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Balancer, address(balancerAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.Curve, address(curveAdapter));
|
||||
router.setProviderAdapter(RouteTypesV2.Provider.OneInch, address(oneInchAdapter));
|
||||
_setDodoOnlyRouting(router);
|
||||
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_cUSDC, dodoProvider, CHAIN138_POOL_CUSDTCUSDC);
|
||||
_setDodoPair(router, CHAIN138_cUSDT, CHAIN138_USDT, dodoProvider, CHAIN138_POOL_CUSDTUSDT);
|
||||
@@ -128,7 +125,10 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
if (balancerVault != address(0) && balancerWethUsdcPoolId != bytes32(0)) {
|
||||
_setBalancerPair(router, CHAIN138_WETH, CHAIN138_USDC, balancerVault, balancerWethUsdcPoolId);
|
||||
}
|
||||
if (balancerVault == address(0) || (balancerWethUsdtPoolId == bytes32(0) && balancerWethUsdcPoolId == bytes32(0))) {
|
||||
if (
|
||||
balancerVault == address(0)
|
||||
|| (balancerWethUsdtPoolId == bytes32(0) && balancerWethUsdcPoolId == bytes32(0))
|
||||
) {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
if (oneInchRouter == address(0)) {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
|
||||
}
|
||||
_disableNonDodoProviders(router);
|
||||
|
||||
vm.stopBroadcast();
|
||||
|
||||
@@ -154,13 +155,25 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
console.log("OneInchRouteExecutorAdapter:", address(oneInchAdapter));
|
||||
}
|
||||
|
||||
function _setDodoPair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
address pool
|
||||
) internal {
|
||||
function _setDodoOnlyRouting(EnhancedSwapRouterV2 router) internal {
|
||||
RouteTypesV2.Provider[] memory providers = new RouteTypesV2.Provider[](1);
|
||||
providers[0] = RouteTypesV2.Provider.Dodo;
|
||||
router.setRoutingConfig(0, providers);
|
||||
router.setRoutingConfig(1, providers);
|
||||
router.setRoutingConfig(2, providers);
|
||||
}
|
||||
|
||||
function _disableNonDodoProviders(EnhancedSwapRouterV2 router) internal {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.UniswapV3, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Balancer, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Curve, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.OneInch, false);
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Partner, false);
|
||||
}
|
||||
|
||||
function _setDodoPair(EnhancedSwapRouterV2 router, address tokenA, address tokenB, address target, address pool)
|
||||
internal
|
||||
{
|
||||
bytes memory providerData = abi.encode(pool);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.Dodo, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.Dodo, target, providerData, true);
|
||||
@@ -179,13 +192,9 @@ contract DeployEnhancedSwapRouterV2 is Script {
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.UniswapV3, target, providerData, true);
|
||||
}
|
||||
|
||||
function _setDodoV3Pair(
|
||||
EnhancedSwapRouterV2 router,
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
address target,
|
||||
address pool
|
||||
) internal {
|
||||
function _setDodoV3Pair(EnhancedSwapRouterV2 router, address tokenA, address tokenB, address target, address pool)
|
||||
internal
|
||||
{
|
||||
bytes memory providerData = abi.encode(pool);
|
||||
router.setProviderRoute(tokenA, tokenB, RouteTypesV2.Provider.DodoV3, target, providerData, true);
|
||||
router.setProviderRoute(tokenB, tokenA, RouteTypesV2.Provider.DodoV3, target, providerData, true);
|
||||
|
||||
@@ -17,7 +17,7 @@ PLAN_JSON="${PLAN_JSON:-$SMOM_ROOT/config/chain138-eth-pmm-liquidity-plan.json}"
|
||||
PROFILE="${PROFILE:-}"
|
||||
EXECUTE="${EXECUTE:-0}"
|
||||
RPC_URL_138="${RPC_URL_138:-${RPC_URL:-http://192.168.11.211:8545}}"
|
||||
DODO_PMM_INTEGRATION_ADDRESS="${CHAIN_138_DODO_PMM_INTEGRATION:-${DODO_PMM_INTEGRATION_ADDRESS:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}"
|
||||
DODO_PMM_INTEGRATION_ADDRESS="${DODO_PMM_INTEGRATION_ADDRESS:-${DODO_PMM_INTEGRATION:-${CHAIN_138_DODO_PMM_INTEGRATION:-0x5BDc62f1ae7D630c37A8B363a1d49845356Ee72d}}}"
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-}"
|
||||
|
||||
command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 1; }
|
||||
@@ -135,6 +135,10 @@ derive_usd_per_eth() {
|
||||
pool="$(hex_to_addr "$(cast call "$DODO_PMM_INTEGRATION_ADDRESS" 'pools(address,address)(address)' "$cusdt" "$quote" --rpc-url "$RPC_URL_138")")"
|
||||
[[ "$pool" != "$ZERO_ADDR" ]] || { echo "0"; return 1; }
|
||||
read -r base_reserve quote_reserve < <(get_pool_reserves "$pool")
|
||||
if [[ "$base_reserve" == "0" || "$quote_reserve" == "0" ]]; then
|
||||
echo "0"
|
||||
return 1
|
||||
fi
|
||||
awk -v base="$base_reserve" -v quote="$quote_reserve" 'BEGIN { printf "%.12f", (base / 1000000) / (quote / 1000000000000000000) }'
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,19 @@ contract MockDodoProviderV2 is ILiquidityProvider {
|
||||
quoteAmount[tokenIn][tokenOut] = amountOut;
|
||||
}
|
||||
|
||||
function getQuote(address tokenIn, address tokenOut, uint256) external view returns (uint256 amountOut, uint256 slippageBps) {
|
||||
function getQuote(address tokenIn, address tokenOut, uint256)
|
||||
external
|
||||
view
|
||||
returns (uint256 amountOut, uint256 slippageBps)
|
||||
{
|
||||
if (!supported[tokenIn][tokenOut]) return (0, 10000);
|
||||
return (quoteAmount[tokenIn][tokenOut], 30);
|
||||
}
|
||||
|
||||
function executeSwap(address tokenIn, address tokenOut, uint256 amountIn, uint256) external returns (uint256 amountOut) {
|
||||
function executeSwap(address tokenIn, address tokenOut, uint256 amountIn, uint256)
|
||||
external
|
||||
returns (uint256 amountOut)
|
||||
{
|
||||
require(supported[tokenIn][tokenOut], "unsupported");
|
||||
amountOut = quoteAmount[tokenIn][tokenOut];
|
||||
ERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);
|
||||
@@ -71,13 +78,11 @@ contract MockUniswapQuoterV2 {
|
||||
quotes[keccak256(abi.encode(tokenIn, tokenOut, fee))] = amountOut;
|
||||
}
|
||||
|
||||
function quoteExactInputSingle(
|
||||
address tokenIn,
|
||||
address tokenOut,
|
||||
uint24 fee,
|
||||
uint256,
|
||||
uint160
|
||||
) external view returns (uint256) {
|
||||
function quoteExactInputSingle(address tokenIn, address tokenOut, uint24 fee, uint256, uint160)
|
||||
external
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return quotes[keccak256(abi.encode(tokenIn, tokenOut, fee))];
|
||||
}
|
||||
|
||||
@@ -93,17 +98,13 @@ contract MockUniswapRouterV2 {
|
||||
quotes[keccak256(abi.encode(tokenIn, tokenOut, fee))] = amountOut;
|
||||
}
|
||||
|
||||
function exactInputSingle(
|
||||
ISwapRouter.ExactInputSingleParams calldata params
|
||||
) external returns (uint256 amountOut) {
|
||||
function exactInputSingle(ISwapRouter.ExactInputSingleParams calldata params) external returns (uint256 amountOut) {
|
||||
amountOut = quotes[keccak256(abi.encode(params.tokenIn, params.tokenOut, params.fee))];
|
||||
ERC20(params.tokenIn).transferFrom(msg.sender, address(this), params.amountIn);
|
||||
ERC20(params.tokenOut).transfer(params.recipient, amountOut);
|
||||
}
|
||||
|
||||
function exactInput(
|
||||
ISwapRouter.ExactInputParams calldata
|
||||
) external pure returns (uint256) {
|
||||
function exactInput(ISwapRouter.ExactInputParams calldata) external pure returns (uint256) {
|
||||
revert("path unsupported in mock");
|
||||
}
|
||||
}
|
||||
@@ -136,22 +137,18 @@ contract MockBalancerVaultV2 {
|
||||
return (address(0xBEEF), 0);
|
||||
}
|
||||
|
||||
function queryBatchSwap(
|
||||
IBalancerVault.SwapKind,
|
||||
IBalancerVault.SingleSwap[] memory,
|
||||
address[] memory
|
||||
) external pure returns (int256[] memory assetDeltas) {
|
||||
function queryBatchSwap(IBalancerVault.SwapKind, IBalancerVault.SingleSwap[] memory, address[] memory)
|
||||
external
|
||||
pure
|
||||
returns (int256[] memory assetDeltas)
|
||||
{
|
||||
assetDeltas = new int256[](0);
|
||||
}
|
||||
|
||||
function getPoolTokens(bytes32 requestedPoolId)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
address[] memory,
|
||||
uint256[] memory,
|
||||
uint256
|
||||
)
|
||||
returns (address[] memory, uint256[] memory, uint256)
|
||||
{
|
||||
require(requestedPoolId == poolId, "bad pool");
|
||||
return (tokens, balances, block.number);
|
||||
@@ -189,26 +186,22 @@ contract MockCurvePoolV2 {
|
||||
contract MockBridgeIntentExecutorV2 is IBridgeIntentExecutor {
|
||||
event Bridged(bytes32 indexed bridgeType, address indexed token, uint256 amount, address recipient);
|
||||
|
||||
function validateBridge(
|
||||
bytes32,
|
||||
bytes calldata,
|
||||
address token,
|
||||
uint256 amount,
|
||||
address recipient
|
||||
) external pure returns (bool ok, string memory reason) {
|
||||
function validateBridge(bytes32, bytes calldata, address token, uint256 amount, address recipient)
|
||||
external
|
||||
pure
|
||||
returns (bool ok, string memory reason)
|
||||
{
|
||||
if (token == address(0) || amount == 0 || recipient == address(0)) {
|
||||
return (false, "invalid bridge request");
|
||||
}
|
||||
return (true, "");
|
||||
}
|
||||
|
||||
function executeBridge(
|
||||
bytes32 bridgeType,
|
||||
bytes calldata,
|
||||
address token,
|
||||
uint256 amount,
|
||||
address recipient
|
||||
) external payable returns (bytes32 referenceId) {
|
||||
function executeBridge(bytes32 bridgeType, bytes calldata, address token, uint256 amount, address recipient)
|
||||
external
|
||||
payable
|
||||
returns (bytes32 referenceId)
|
||||
{
|
||||
ERC20(token).transferFrom(msg.sender, address(this), amount);
|
||||
emit Bridged(bridgeType, token, amount, recipient);
|
||||
return keccak256(abi.encode(bridgeType, token, amount, recipient));
|
||||
@@ -250,11 +243,11 @@ contract MockD3MMV2 {
|
||||
quoteAmount[tokenIn][tokenOut] = amountOut;
|
||||
}
|
||||
|
||||
function querySellTokens(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 fromAmount
|
||||
) external view returns (uint256 payFromAmount, uint256 receiveToAmount, uint256 vusdAmount, uint256 swapFee, uint256 mtFee) {
|
||||
function querySellTokens(address fromToken, address toToken, uint256 fromAmount)
|
||||
external
|
||||
view
|
||||
returns (uint256 payFromAmount, uint256 receiveToAmount, uint256 vusdAmount, uint256 swapFee, uint256 mtFee)
|
||||
{
|
||||
payFromAmount = fromAmount;
|
||||
receiveToAmount = quoteAmount[fromToken][toToken];
|
||||
vusdAmount = 0;
|
||||
@@ -301,14 +294,8 @@ contract MockD3ProxyV2 {
|
||||
) external payable returns (uint256 receiveToAmount) {
|
||||
require(deadLine >= block.timestamp, "expired");
|
||||
SwapCallbackData memory swapData = SwapCallbackData({data: data, payer: msg.sender});
|
||||
receiveToAmount = MockD3MMV2(pool).sellToken(
|
||||
to,
|
||||
fromToken,
|
||||
toToken,
|
||||
fromAmount,
|
||||
minReceiveAmount,
|
||||
abi.encode(swapData)
|
||||
);
|
||||
receiveToAmount =
|
||||
MockD3MMV2(pool).sellToken(to, fromToken, toToken, fromAmount, minReceiveAmount, abi.encode(swapData));
|
||||
}
|
||||
|
||||
function d3MMSwapCallBack(address token, uint256 value, bytes calldata callbackData) external {
|
||||
@@ -589,6 +576,39 @@ contract EnhancedSwapRouterV2Test is Test {
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testQuoteConfiguredProviderQuotesSingleDodoRoute() public view {
|
||||
RouteTypesV2.ProviderQuote memory quote =
|
||||
router.quoteConfiguredProvider(address(weth), address(usdt), 1 ether, RouteTypesV2.Provider.Dodo);
|
||||
|
||||
assertEq(uint256(quote.provider), uint256(RouteTypesV2.Provider.Dodo));
|
||||
assertEq(quote.target, address(dodoProvider));
|
||||
assertEq(quote.amountOut, 1_800 ether);
|
||||
assertEq(quote.estimatedGas, 140000);
|
||||
assertTrue(quote.executable);
|
||||
assertEq(abi.decode(quote.providerData, (address)), address(0xA11CE));
|
||||
}
|
||||
|
||||
function testQuoteConfiguredProviderRevertsWhenProviderDisabled() public {
|
||||
router.setProviderEnabled(RouteTypesV2.Provider.Dodo, false);
|
||||
|
||||
vm.expectRevert(EnhancedSwapRouterV2.ProviderDisabled.selector);
|
||||
router.quoteConfiguredProvider(address(weth), address(usdt), 1 ether, RouteTypesV2.Provider.Dodo);
|
||||
}
|
||||
|
||||
function testQuoteConfiguredProviderRevertsWhenRouteMissing() public {
|
||||
vm.expectRevert(EnhancedSwapRouterV2.RouteNotConfigured.selector);
|
||||
router.quoteConfiguredProvider(address(weth), address(dai), 1 ether, RouteTypesV2.Provider.Dodo);
|
||||
}
|
||||
|
||||
function testQuoteConfiguredProvidersStillDiscoversConfiguredRoutes() public view {
|
||||
RouteTypesV2.ProviderQuote[] memory quotes =
|
||||
router.quoteConfiguredProviders(address(weth), address(usdt), 1 ether);
|
||||
|
||||
assertEq(quotes.length, 1);
|
||||
assertEq(uint256(quotes[0].provider), uint256(RouteTypesV2.Provider.Dodo));
|
||||
assertEq(quotes[0].amountOut, 1_800 ether);
|
||||
}
|
||||
|
||||
function testMultiLegExecutionUsesPreviousLegOutput() public {
|
||||
vm.startPrank(user);
|
||||
weth.approve(address(router), 1 ether);
|
||||
|
||||
Reference in New Issue
Block a user