Files
smom-dbis-138/contracts/cw-settlement/CWBuybackExecutor.sol

94 lines
3.2 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 "../bridge/integration/ICWReserveVerifier.sol";
interface ICWBurnable {
function burn(uint256 amount) external;
}
/**
* @title CWBuybackExecutor
* @notice Routes fee revenue into cW buybacks; burns purchased tokens or locks in treasury.
*/
contract CWBuybackExecutor is AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
ICWReserveVerifier public reserveVerifier;
address public treasuryLock;
bool public burnOnBuyback = true;
event ReserveVerifierUpdated(address indexed verifier);
event TreasuryLockUpdated(address indexed treasuryLock);
event BurnModeUpdated(bool burnOnBuyback);
event BuybackExecuted(address indexed paymentToken, address indexed cwToken, uint256 paid, uint256 received, bool burned);
error ZeroAddress();
error ReserveCheckFailed();
constructor(address admin, address reserveVerifier_, address treasuryLock_) {
if (admin == address(0)) {
revert ZeroAddress();
}
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(EXECUTOR_ROLE, admin);
reserveVerifier = ICWReserveVerifier(reserveVerifier_);
treasuryLock = treasuryLock_;
}
function setReserveVerifier(address verifier_) external onlyRole(DEFAULT_ADMIN_ROLE) {
reserveVerifier = ICWReserveVerifier(verifier_);
emit ReserveVerifierUpdated(verifier_);
}
function setTreasuryLock(address treasuryLock_) external onlyRole(DEFAULT_ADMIN_ROLE) {
treasuryLock = treasuryLock_;
emit TreasuryLockUpdated(treasuryLock_);
}
function setBurnOnBuyback(bool enabled) external onlyRole(DEFAULT_ADMIN_ROLE) {
burnOnBuyback = enabled;
emit BurnModeUpdated(enabled);
}
/**
* @notice Operator delivers pre-purchased cW tokens; contract burns or locks them after reserve check.
*/
function executeBuyback(
address canonicalToken,
uint64 destinationChainSelector,
address cwToken,
address paymentToken,
uint256 paymentAmount,
uint256 cwAmount
) external onlyRole(EXECUTOR_ROLE) nonReentrant {
if (address(reserveVerifier) != address(0)) {
if (!reserveVerifier.verifyLock(canonicalToken, destinationChainSelector, 0)) {
revert ReserveCheckFailed();
}
}
if (paymentAmount > 0 && paymentToken != address(0)) {
IERC20(paymentToken).safeTransferFrom(msg.sender, address(this), paymentAmount);
}
IERC20(cwToken).safeTransferFrom(msg.sender, address(this), cwAmount);
bool burned = false;
if (burnOnBuyback) {
ICWBurnable(cwToken).burn(cwAmount);
burned = true;
} else if (treasuryLock != address(0)) {
IERC20(cwToken).safeTransfer(treasuryLock, cwAmount);
}
emit BuybackExecuted(paymentToken, cwToken, paymentAmount, cwAmount, burned);
}
}