96 lines
3.3 KiB
Solidity
96 lines
3.3 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 "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
|
import "../ccip/CCIPWETH9Bridge.sol";
|
|
|
|
/**
|
|
* @title CcipBridgeAdapter138
|
|
* @notice Export-only module: only WETH9 to Ethereum Mainnet; only callable by StrategyExecutor138.
|
|
* @dev Destination selector fixed to 5009297550715157269. Gate: exportsEnabled must be true.
|
|
* See docs/treasury/EXECUTOR_ALLOWLIST_MATRIX.md and EXPORT_STATE_MACHINE.md.
|
|
*/
|
|
contract CcipBridgeAdapter138 is AccessControl, ReentrancyGuard {
|
|
using SafeERC20 for IERC20;
|
|
|
|
uint64 public constant MAINNET_SELECTOR = 5009297550715157269;
|
|
|
|
address public immutable weth9;
|
|
address public immutable bridgeWeth9;
|
|
address public immutable receiverMainnet;
|
|
address public strategyExecutor;
|
|
bool private _executorSet;
|
|
|
|
bool public exportsEnabled;
|
|
|
|
event ExportInitiated(bytes32 indexed messageId, uint256 amount);
|
|
event ExportsEnabledSet(bool enabled);
|
|
|
|
error ExportsDisabled();
|
|
error OnlyExecutor();
|
|
error ZeroAmount();
|
|
error DeadlineExpired();
|
|
error InsufficientOutput();
|
|
|
|
constructor(
|
|
address _weth9,
|
|
address _bridgeWeth9,
|
|
address _receiverMainnet,
|
|
address admin
|
|
) {
|
|
if (_weth9 == address(0) || _bridgeWeth9 == address(0) || _receiverMainnet == address(0)) {
|
|
revert("CcipBridgeAdapter138: zero address");
|
|
}
|
|
weth9 = _weth9;
|
|
bridgeWeth9 = _bridgeWeth9;
|
|
receiverMainnet = _receiverMainnet;
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
}
|
|
|
|
function setStrategyExecutor(address _executor) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
if (_executorSet) revert("CcipBridgeAdapter138: executor already set");
|
|
if (_executor == address(0)) revert("CcipBridgeAdapter138: zero executor");
|
|
_executorSet = true;
|
|
strategyExecutor = _executor;
|
|
}
|
|
|
|
function setExportsEnabled(bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
exportsEnabled = enabled;
|
|
emit ExportsEnabledSet(enabled);
|
|
}
|
|
|
|
/**
|
|
* @notice Send WETH9 to Ethereum Mainnet. Only callable by StrategyExecutor138.
|
|
* @param amount Amount of WETH9 to send.
|
|
* @param minAmount Unused; reserved for future slippage/quote checks.
|
|
* @param deadline Revert if block.timestamp > deadline.
|
|
*/
|
|
function sendWeth9ToMainnet(uint256 amount, uint256 minAmount, uint256 deadline)
|
|
external
|
|
payable
|
|
nonReentrant
|
|
returns (bytes32 messageId)
|
|
{
|
|
if (msg.sender != strategyExecutor) revert OnlyExecutor();
|
|
if (!exportsEnabled) revert ExportsDisabled();
|
|
if (amount == 0) revert ZeroAmount();
|
|
if (block.timestamp > deadline) revert DeadlineExpired();
|
|
if (minAmount > 0 && amount < minAmount) revert InsufficientOutput();
|
|
|
|
IERC20(weth9).safeTransferFrom(msg.sender, address(this), amount);
|
|
IERC20(weth9).approve(bridgeWeth9, amount);
|
|
|
|
messageId = CCIPWETH9Bridge(payable(bridgeWeth9)).sendCrossChain{value: msg.value}(
|
|
MAINNET_SELECTOR,
|
|
receiverMainnet,
|
|
amount
|
|
);
|
|
|
|
emit ExportInitiated(messageId, amount);
|
|
return messageId;
|
|
}
|
|
}
|