Files
smom-dbis-138/contracts/relay/CCIPRelayRouter.sol
defiQUG 11c97777d4
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m11s
CI/CD Pipeline / Security Scanning (push) Has been cancelled
CI/CD Pipeline / Lint and Format (push) Has been cancelled
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 1m4s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 31s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 29s
Verify Deployment / Verify Deployment (push) Failing after 57s
feat(chain138): Monad CCIP, token aggregation OMNL gates, HYBX client, and PMM deploy updates.
Relay router, reserve system, oracle publisher, token-aggregation compliance middleware, and Monad deployment scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-18 00:11:33 -07:00

152 lines
5.3 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "../ccip/IRouterClient.sol";
import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
import {IAny2EVMMessageReceiver} from "@chainlink/contracts-ccip/contracts/interfaces/IAny2EVMMessageReceiver.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title CCIP Relay Router
* @notice Relay router that forwards CCIP messages from off-chain relay to bridge contracts
* @dev This contract acts as a relay endpoint on the destination chain
*/
contract CCIPRelayRouter is AccessControl {
bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");
// Mapping of bridge contracts that can receive messages
mapping(address => bool) public authorizedBridges;
event MessageRelayed(
bytes32 indexed messageId,
uint64 indexed sourceChainSelector,
address indexed bridge,
address recipient,
uint256 amount
);
event BridgeAuthorized(address indexed bridge);
event BridgeRevoked(address indexed bridge);
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
* @notice Authorize a bridge contract to receive relayed messages
*/
function authorizeBridge(address bridge) external onlyRole(DEFAULT_ADMIN_ROLE) {
authorizedBridges[bridge] = true;
emit BridgeAuthorized(bridge);
}
/**
* @notice Revoke authorization for a bridge contract
*/
function revokeBridge(address bridge) external onlyRole(DEFAULT_ADMIN_ROLE) {
authorizedBridges[bridge] = false;
emit BridgeRevoked(bridge);
}
/**
* @notice Grant relayer role to an address
*/
function grantRelayerRole(address relayer) external onlyRole(DEFAULT_ADMIN_ROLE) {
_grantRole(RELAYER_ROLE, relayer);
}
/**
* @notice Revoke relayer role from an address
*/
function revokeRelayerRole(address relayer) external onlyRole(DEFAULT_ADMIN_ROLE) {
_revokeRole(RELAYER_ROLE, relayer);
}
/**
* @notice Relay a CCIP message to a bridge contract
* @param bridge The bridge contract address to receive the message
* @param message The CCIP message to relay
*/
function relayMessage(
address bridge,
IRouterClient.Any2EVMMessage calldata message
) external onlyRole(RELAYER_ROLE) {
require(authorizedBridges[bridge], "CCIPRelayRouter: bridge not authorized");
// CWMultiTokenBridgeL2 and official CCIP receivers use Client.Any2EVMMessage
// (destTokenAmounts: EVMTokenAmount[]). Legacy CCIPRelayBridge uses IRouterClient
// Any2EVMMessage (tokenAmounts with amountType). Try Client format first, then legacy.
Client.Any2EVMMessage memory clientMessage = _toClientMessage(message);
(bool success, bytes memory returnData) = bridge.call(
abi.encodeCall(IAny2EVMMessageReceiver.ccipReceive, (clientMessage))
);
if (!success) {
(success, returnData) = bridge.call(
abi.encodeWithSignature(
"ccipReceive((bytes32,uint64,bytes,bytes,(address,uint256,uint8)[]))",
message
)
);
}
if (!success && returnData.length > 0) {
assembly {
revert(add(returnData, 32), mload(returnData))
}
}
require(success, "CCIPRelayRouter: ccipReceive failed");
// If we get here, the call succeeded. Decode common payload shapes without
// reverting the full relay transaction on non-WETH bridge payloads.
(address recipient, uint256 amount) = _decodeRecipientAndAmount(message.data);
emit MessageRelayed(
message.messageId,
message.sourceChainSelector,
bridge,
recipient,
amount
);
}
function _decodeRecipientAndAmount(bytes calldata data)
internal
pure
returns (address recipient, uint256 amount)
{
if (data.length == 64) {
return abi.decode(data, (address, uint256));
}
if (data.length == 96) {
(, recipient, amount) = abi.decode(data, (address, address, uint256));
return (recipient, amount);
}
if (data.length == 128) {
(recipient, amount, , ) = abi.decode(data, (address, uint256, address, uint256));
return (recipient, amount);
}
return (address(0), 0);
}
function _toClientMessage(
IRouterClient.Any2EVMMessage calldata message
) internal pure returns (Client.Any2EVMMessage memory clientMessage) {
Client.EVMTokenAmount[] memory destAmounts = new Client.EVMTokenAmount[](message.tokenAmounts.length);
for (uint256 i = 0; i < message.tokenAmounts.length; i++) {
destAmounts[i] = Client.EVMTokenAmount({
token: message.tokenAmounts[i].token,
amount: message.tokenAmounts[i].amount
});
}
clientMessage = Client.Any2EVMMessage({
messageId: message.messageId,
sourceChainSelector: message.sourceChainSelector,
sender: message.sender,
data: message.data,
destTokenAmounts: destAmounts
});
}
}