64 lines
2.1 KiB
Solidity
64 lines
2.1 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";
|
|
|
|
/**
|
|
* @title EtherlinkRelayReceiver
|
|
* @notice Relay-compatible receiver on Etherlink (chain 42793). Accepts relayMintOrUnlock from off-chain relay.
|
|
* @dev When CCIP does not support Etherlink, custom relay monitors source and calls this contract.
|
|
* Idempotency via messageId; only RELAYER_ROLE can call.
|
|
*/
|
|
contract EtherlinkRelayReceiver is AccessControl, ReentrancyGuard {
|
|
using SafeERC20 for IERC20;
|
|
|
|
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
|
|
|
|
mapping(bytes32 => bool) public processed;
|
|
|
|
event RelayMintOrUnlock(
|
|
bytes32 indexed messageId,
|
|
address indexed token,
|
|
address indexed recipient,
|
|
uint256 amount
|
|
);
|
|
|
|
constructor(address admin) {
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
_grantRole(RELAYER_ROLE, admin);
|
|
}
|
|
|
|
/**
|
|
* @notice Mint or unlock tokens to recipient. Only relayer; idempotent per messageId.
|
|
* @param messageId Source chain message id (for idempotency).
|
|
* @param token Token address (address(0) for native).
|
|
* @param recipient Recipient on Etherlink.
|
|
* @param amount Amount to transfer.
|
|
*/
|
|
function relayMintOrUnlock(
|
|
bytes32 messageId,
|
|
address token,
|
|
address recipient,
|
|
uint256 amount
|
|
) external onlyRole(RELAYER_ROLE) nonReentrant {
|
|
require(!processed[messageId], "already processed");
|
|
require(recipient != address(0), "zero recipient");
|
|
require(amount > 0, "zero amount");
|
|
processed[messageId] = true;
|
|
|
|
if (token == address(0)) {
|
|
(bool sent,) = payable(recipient).call{value: amount}("");
|
|
require(sent, "transfer failed");
|
|
} else {
|
|
IERC20(token).safeTransfer(recipient, amount);
|
|
}
|
|
|
|
emit RelayMintOrUnlock(messageId, token, recipient, amount);
|
|
}
|
|
|
|
receive() external payable {}
|
|
}
|