Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[submodule "evm/lib/solidity-bytes-utils"]
path = evm/lib/solidity-bytes-utils
url = https://github.com/GNSPS/solidity-bytes-utils
[submodule "evm/lib/example-messaging-executor"]
path = evm/lib/example-messaging-executor
url = https://github.com/wormholelabs-xyz/example-messaging-executor
1 change: 1 addition & 0 deletions evm/lib/example-messaging-executor
81 changes: 81 additions & 0 deletions evm/src/NttManager/ManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import "../libraries/Implementation.sol";

import "../interfaces/ITransceiver.sol";
import "../interfaces/IManagerBase.sol";
import "../interfaces/IMsgReceiver.sol";

import "./TransceiverRegistry.sol";

abstract contract ManagerBase is
IManagerBase,
IMsgReceiver,
TransceiverRegistry,
PausableOwnable,
ReentrancyGuardUpgradeable,
Expand Down Expand Up @@ -464,6 +466,85 @@ abstract contract ManagerBase is
_getMessageSequenceStorage().num++;
}

function attestationReceived(
uint16 sourceChainId,
bytes32 sourceManagerAddress,
TransceiverStructs.NttManagerMessage memory payload
) external onlyTransceiver whenNotPaused {
_verifyPeer(sourceChainId, sourceManagerAddress);

// Compute manager message digest and record transceiver attestation.
bytes32 nttManagerMessageHash = _recordTransceiverAttestation(sourceChainId, payload);

if (isMessageApproved(nttManagerMessageHash)) {
executeMsg(sourceChainId, sourceManagerAddress, payload);
}
}

function executeMsg(
uint16 sourceChainId,
bytes32 sourceNttManagerAddress,
TransceiverStructs.NttManagerMessage memory message
) public whenNotPaused {
(bytes32 digest, bool alreadyExecuted) =
_isMessageExecuted(sourceChainId, sourceNttManagerAddress, message);

if (alreadyExecuted) {
return;
}

_handleMsg(sourceChainId, sourceNttManagerAddress, message, digest);
}

/// @dev Override this function to handle your messages.
function _handleMsg(
uint16 sourceChainId,
bytes32 sourceManagerAddress,
TransceiverStructs.NttManagerMessage memory message,
bytes32 digest
) internal virtual {}

function _sendMessage(
uint64 sequence,
uint16 recipientChain,
bytes32 recipientManagerAddress,
bytes32 refundAddress,
address sender,
bytes memory payload,
bytes memory transceiverInstructions
) internal returns (uint256 totalPriceQuote, bytes memory encodedNttManagerPayload) {
// verify chain has not forked
checkFork(evmChainId);

address[] memory enabledTransceivers;
TransceiverStructs.TransceiverInstruction[] memory instructions;
uint256[] memory priceQuotes;
(enabledTransceivers, instructions, priceQuotes, totalPriceQuote) =
_prepareForTransfer(recipientChain, transceiverInstructions);
(recipientChain, transceiverInstructions);

// construct the NttManagerMessage payload
encodedNttManagerPayload = TransceiverStructs.encodeNttManagerMessage(
TransceiverStructs.NttManagerMessage(
bytes32(uint256(sequence)), toWormholeFormat(sender), payload
)
);

// send the message
_sendMessageToTransceivers(
recipientChain,
refundAddress,
recipientManagerAddress,
priceQuotes,
instructions,
enabledTransceivers,
encodedNttManagerPayload
);
}

/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view virtual;

/// ============== Invariants =============================================

/// @dev When we add new immutables, this function should be updated
Expand Down
138 changes: 138 additions & 0 deletions evm/src/NttManager/MsgManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;

import "wormhole-solidity-sdk/Utils.sol";
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";

import "../interfaces/IMsgManager.sol";
import "../interfaces/ITransceiver.sol";
import "../libraries/TransceiverHelpers.sol";

import {ManagerBase} from "./ManagerBase.sol";

contract MsgManager is IMsgManager, ManagerBase {
string public constant MSG_MANAGER_VERSION = "1.0.0";

// =============== Setup =================================================================

constructor(
uint16 _chainId
) ManagerBase(address(0), Mode.LOCKING, _chainId) {}

function _initialize() internal virtual override {
_init();
_checkThresholdInvariants();
_checkTransceiversInvariants();
}

function _init() internal onlyInitializing {
// check if the owner is the deployer of this contract
if (msg.sender != deployer) {
revert UnexpectedDeployer(deployer, msg.sender);
}
if (msg.value != 0) {
revert UnexpectedMsgValue();
}
__PausedOwnable_init(msg.sender, msg.sender);
__ReentrancyGuard_init();
}

// =============== Storage ==============================================================

bytes32 private constant PEERS_SLOT = bytes32(uint256(keccak256("mmgr.peers")) - 1);

// =============== Storage Getters/Setters ==============================================

function _getPeersStorage()
internal
pure
returns (mapping(uint16 => MsgManagerPeer) storage $)
{
uint256 slot = uint256(PEERS_SLOT);
assembly ("memory-safe") {
$.slot := slot
}
}

// =============== Public Getters ========================================================

/// @inheritdoc IMsgManager
function getPeer(
uint16 chainId_
) external view returns (MsgManagerPeer memory) {
return _getPeersStorage()[chainId_];
}

// =============== Admin ==============================================================

/// @inheritdoc IMsgManager
function setPeer(uint16 peerChainId, bytes32 peerAddress) public onlyOwner {
if (peerChainId == 0) {
revert InvalidPeerChainIdZero();
}
if (peerAddress == bytes32(0)) {
revert InvalidPeerZeroAddress();
}
if (peerChainId == chainId) {
revert InvalidPeerSameChainId();
}

MsgManagerPeer memory oldPeer = _getPeersStorage()[peerChainId];

_getPeersStorage()[peerChainId].peerAddress = peerAddress;

emit PeerUpdated(peerChainId, oldPeer.peerAddress, peerAddress);
}

/// ============== Invariants =============================================

/// @dev When we add new immutables, this function should be updated
function _checkImmutables() internal view virtual override {
super._checkImmutables();
}

// ==================== External Interface ===============================================

/// @inheritdoc IMsgManager
function sendMessage(
uint16 recipientChain,
bytes32 refundAddress,
bytes calldata payload,
bytes memory transceiverInstructions
) external payable nonReentrant whenNotPaused returns (uint64 sequence) {
sequence = _useMessageSequence();

bytes32 recipientAddress = _getPeersStorage()[recipientChain].peerAddress;

(uint256 totalPriceQuote, bytes memory encodedNttManagerPayload) = _sendMessage(
sequence,
recipientChain,
recipientAddress,
refundAddress,
msg.sender,
payload,
transceiverInstructions
);

emit MessageSent(
recipientChain, recipientAddress, sequence, totalPriceQuote, encodedNttManagerPayload
);
}

/// @dev Override this function to handle your messages.
function _handleMsg(
uint16 sourceChainId,
bytes32 sourceManagerAddress,
TransceiverStructs.NttManagerMessage memory message,
bytes32 digest
) internal virtual override {}

// ==================== Internal Helpers ===============================================

/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view override {
if (_getPeersStorage()[sourceChainId].peerAddress != peerAddress) {
revert InvalidPeer(sourceChainId, peerAddress);
}
}
}
Loading
Loading