123 lines
4.7 KiB
Solidity
123 lines
4.7 KiB
Solidity
// 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);
|
|
}
|
|
}
|