Files
smom-dbis-138/contracts/bridge/adapters/evm/ZedxionAdapter.sol

159 lines
5.4 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 "../../interfaces/IChainAdapter.sol";
import "../../interfaces/IZedxionTransport.sol";
/**
* @title ZedxionAdapter
* @notice Bridge adapter for ZEDXION Smart Chain (ZED20), chainId 83872.
* @dev CCIP does not list 83872. Wire ZedxionCustomBridge via setZedxionTransport().
*/
contract ZedxionAdapter is IChainAdapter, AccessControl, ReentrancyGuard {
using SafeERC20 for IERC20;
bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
uint256 public constant ZEDXION_CHAIN_ID = 83872;
IZedxionTransport public zedxionTransport;
bool public isActive;
uint256 public bridgeFee;
mapping(bytes32 => BridgeRequest) public bridgeRequests;
mapping(address => uint256) public nonces;
event ZedxionAdapterBridge(
bytes32 indexed requestId,
address indexed sender,
address indexed token,
uint256 amount,
address recipient
);
event ZedxionBridgeConfirmed(bytes32 indexed requestId, bytes32 indexed destinationTxHash);
constructor(address admin) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(BRIDGE_OPERATOR_ROLE, admin);
isActive = true;
bridgeFee = 0;
}
function setZedxionTransport(address transport) external onlyRole(DEFAULT_ADMIN_ROLE) {
zedxionTransport = IZedxionTransport(transport);
}
function setActive(bool active) external onlyRole(DEFAULT_ADMIN_ROLE) {
isActive = active;
}
function setBridgeFee(uint256 fee) external onlyRole(DEFAULT_ADMIN_ROLE) {
bridgeFee = fee;
}
function getChainType() external pure override returns (string memory) {
return "EVM";
}
function getChainIdentifier() external pure override returns (uint256 chainId, string memory identifier) {
return (ZEDXION_CHAIN_ID, "ZEDXION");
}
function validateDestination(bytes calldata destination) external pure override returns (bool) {
if (destination.length != 20) return false;
return address(bytes20(destination)) != address(0);
}
function bridge(
address token,
uint256 amount,
bytes calldata destination,
bytes calldata /* recipient */
) external payable override nonReentrant returns (bytes32 requestId) {
require(isActive, "ZedxionAdapter: inactive");
require(amount > 0, "ZedxionAdapter: zero amount");
require(address(zedxionTransport) != address(0), "ZedxionAdapter: transport not set");
require(this.validateDestination(destination), "ZedxionAdapter: invalid destination");
address recipientAddr = address(bytes20(destination));
requestId = keccak256(abi.encodePacked(
msg.sender,
token,
amount,
recipientAddr,
nonces[msg.sender]++,
block.timestamp
));
if (token == address(0)) {
require(msg.value >= amount, "ZedxionAdapter: insufficient value");
} else {
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
IERC20(token).forceApprove(address(zedxionTransport), amount);
}
bridgeRequests[requestId] = BridgeRequest({
sender: msg.sender,
token: token,
amount: amount,
destinationData: destination,
requestId: requestId,
status: BridgeStatus.Locked,
createdAt: block.timestamp,
completedAt: 0
});
zedxionTransport.bridgeToZedxion{value: msg.value}(token, amount, recipientAddr);
emit ZedxionAdapterBridge(requestId, msg.sender, token, amount, recipientAddr);
return requestId;
}
function getBridgeStatus(bytes32 requestId) external view override returns (BridgeRequest memory) {
return bridgeRequests[requestId];
}
function cancelBridge(bytes32 requestId) external override returns (bool) {
BridgeRequest storage request = bridgeRequests[requestId];
require(
request.status == BridgeStatus.Pending || request.status == BridgeStatus.Locked,
"ZedxionAdapter: cannot cancel"
);
require(msg.sender == request.sender, "ZedxionAdapter: not sender");
if (request.token == address(0)) {
payable(request.sender).transfer(request.amount);
} else {
IERC20(request.token).safeTransfer(request.sender, request.amount);
}
request.status = BridgeStatus.Cancelled;
return true;
}
function estimateFee(
address /* token */,
uint256 /* amount */,
bytes calldata /* destination */
) external view override returns (uint256) {
return bridgeFee;
}
function confirmBridge(bytes32 requestId, bytes32 destinationTxHash)
external
onlyRole(BRIDGE_OPERATOR_ROLE)
{
BridgeRequest storage request = bridgeRequests[requestId];
require(request.status == BridgeStatus.Locked, "ZedxionAdapter: invalid status");
request.status = BridgeStatus.Confirmed;
request.completedAt = block.timestamp;
emit ZedxionBridgeConfirmed(requestId, destinationTxHash);
}
}