Add DODO-only EnhancedSwapRouterV2 routing helpers
This commit is contained in:
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