synapsecns/sanguine

View on GitHub
packages/contracts-core/contracts/client/MessageRecipient.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

// ══════════════════════════════ LIBRARY IMPORTS ══════════════════════════════
import {
    CallerNotDestination,
    IncorrectNonce,
    IncorrectSender,
    IncorrectRecipient,
    ZeroProofMaturity
} from "../libs/Errors.sol";
import {Request, RequestLib} from "../libs/stack/Request.sol";
// ═════════════════════════════ INTERNAL IMPORTS ══════════════════════════════
import {InterfaceOrigin} from "../interfaces/InterfaceOrigin.sol";
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";

abstract contract MessageRecipient is IMessageRecipient {
    struct MessageRequest {
        uint96 gasDrop;
        uint64 gasLimit;
        uint32 version;
    }

    /// @notice Local chain Origin: used for sending messages
    address public immutable origin;

    /// @notice Local chain Destination: used for receiving messages
    address public immutable destination;

    constructor(address origin_, address destination_) {
        origin = origin_;
        destination = destination_;
    }

    /// @inheritdoc IMessageRecipient
    function receiveBaseMessage(
        uint32 origin_,
        uint32 nonce,
        bytes32 sender,
        uint256 proofMaturity,
        uint32 version,
        bytes memory content
    ) external payable {
        if (msg.sender != destination) revert CallerNotDestination();
        if (nonce == 0) revert IncorrectNonce();
        if (sender == 0) revert IncorrectSender();
        if (proofMaturity == 0) revert ZeroProofMaturity();
        _receiveBaseMessageUnsafe(origin_, nonce, sender, proofMaturity, version, content);
    }

    /**
     * @dev Child contracts should implement the logic for receiving a Base Message in an "unsafe way".
     * Following checks HAVE been performed:
     *  - receiveBaseMessage() was called by Destination (i.e. this is a legit base message).
     *  - Nonce is not zero.
     *  - Message sender on origin chain is not a zero address.
     *  - Proof maturity is not zero.
     * Following checks HAVE NOT been performed (thus "unsafe"):
     *  - Message sender on origin chain could be anything non-zero at this point.
     *  - Proof maturity could be anything non-zero at this point.
     */
    function _receiveBaseMessageUnsafe(
        uint32 origin_,
        uint32 nonce,
        bytes32 sender,
        uint256 proofMaturity,
        uint32 version,
        bytes memory content
    ) internal virtual;

    /**
     * @dev Sends a message to given destination chain. Full `msg.value` is used to pay for the message tips.
     * `_getMinimumTipsValue()` could be used to calculate the minimum required tips value, and should be also
     * exposed as a public view function to estimate the tips value before sending a message off-chain.
     * This function is not exposed in MessageRecipient, as the message encoding is implemented by the child contract.
     * @param destination_          Domain of the destination chain
     * @param recipient             Address of the recipient on destination chain
     * @param optimisticPeriod      Optimistic period for the message
     * @param tipsValue             Tips to be paid for sending the message
     * @param request               Message execution request on destination chain
     * @param content               The message content
     */
    function _sendBaseMessage(
        uint32 destination_,
        bytes32 recipient,
        uint32 optimisticPeriod,
        uint256 tipsValue,
        MessageRequest memory request,
        bytes memory content
    ) internal returns (uint32 messageNonce, bytes32 messageHash) {
        if (recipient == 0) revert IncorrectRecipient();
        return InterfaceOrigin(origin).sendBaseMessage{value: tipsValue}(
            destination_, recipient, optimisticPeriod, _encodeRequest(request), content
        );
    }

    /**
     * @dev Returns the minimum tips value for sending a message to given destination chain.
     * @param destination_          Domain of the destination chain
     * @param request               Message execution request on destination chain
     * @param contentLength         Length of the message content
     */
    function _getMinimumTipsValue(uint32 destination_, MessageRequest memory request, uint256 contentLength)
        internal
        view
        returns (uint256 tipsValue)
    {
        return InterfaceOrigin(origin).getMinimumTipsValue(destination_, _encodeRequest(request), contentLength);
    }

    /**
     * @dev Encodes a message execution request into format that Origin contract is using.
     * @param request               Message execution request on destination chain
     * @return paddedRequest        Encoded request
     */
    function _encodeRequest(MessageRequest memory request) internal pure returns (uint256 paddedRequest) {
        return Request.unwrap(RequestLib.encodeRequest(request.gasDrop, request.gasLimit, request.version));
    }
}