Skip to content

Commit 47d049c

Browse files
committed
evm: Add MsgManager
1 parent 9c3fc7e commit 47d049c

13 files changed

+1122
-97
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@
1010
[submodule "evm/lib/solidity-bytes-utils"]
1111
path = evm/lib/solidity-bytes-utils
1212
url = https://github.com/GNSPS/solidity-bytes-utils
13+
[submodule "evm/lib/example-messaging-executor"]
14+
path = evm/lib/example-messaging-executor
15+
url = https://github.com/wormholelabs-xyz/example-messaging-executor

evm/lib/example-messaging-executor

evm/src/NttManager/ManagerBase.sol

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,83 @@ abstract contract ManagerBase is
464464
_getMessageSequenceStorage().num++;
465465
}
466466

467+
function attestationReceived(
468+
uint16 sourceChainId,
469+
bytes32 sourceManagerAddress,
470+
TransceiverStructs.NttManagerMessage memory payload
471+
) external onlyTransceiver whenNotPaused {
472+
_verifyPeer(sourceChainId, sourceManagerAddress);
473+
474+
// Compute manager message digest and record transceiver attestation.
475+
bytes32 nttManagerMessageHash = _recordTransceiverAttestation(sourceChainId, payload);
476+
477+
if (isMessageApproved(nttManagerMessageHash)) {
478+
executeMsg(sourceChainId, sourceManagerAddress, payload);
479+
}
480+
}
481+
482+
function executeMsg(
483+
uint16 sourceChainId,
484+
bytes32 sourceNttManagerAddress,
485+
TransceiverStructs.NttManagerMessage memory message
486+
) public whenNotPaused {
487+
(bytes32 digest, bool alreadyExecuted) =
488+
_isMessageExecuted(sourceChainId, sourceNttManagerAddress, message);
489+
490+
if (alreadyExecuted) {
491+
return;
492+
}
493+
494+
_handleMsg(sourceChainId, sourceNttManagerAddress, message, digest);
495+
}
496+
497+
/// @dev Override this function to handle your messages.
498+
function _handleMsg(
499+
uint16 sourceChainId,
500+
bytes32 sourceManagerAddress,
501+
TransceiverStructs.NttManagerMessage memory message,
502+
bytes32 digest
503+
) internal virtual {}
504+
505+
function _sendMessage(
506+
uint16 recipientChain,
507+
bytes32 recipientManagerAddress,
508+
uint64 sequence,
509+
bytes memory payload,
510+
bytes memory transceiverInstructions
511+
) internal returns (uint256 totalPriceQuote, bytes memory encodedNttManagerPayload) {
512+
// verify chain has not forked
513+
checkFork(evmChainId);
514+
515+
address[] memory enabledTransceivers;
516+
TransceiverStructs.TransceiverInstruction[] memory instructions;
517+
uint256[] memory priceQuotes;
518+
(enabledTransceivers, instructions, priceQuotes, totalPriceQuote) =
519+
_prepareForTransfer(recipientChain, transceiverInstructions);
520+
(recipientChain, transceiverInstructions);
521+
522+
// construct the NttManagerMessage payload
523+
encodedNttManagerPayload = TransceiverStructs.encodeNttManagerMessage(
524+
TransceiverStructs.NttManagerMessage(
525+
bytes32(uint256(sequence)), toWormholeFormat(msg.sender), payload
526+
)
527+
);
528+
529+
// send the message
530+
_sendMessageToTransceivers(
531+
recipientChain,
532+
recipientManagerAddress, // refundAddress
533+
recipientManagerAddress,
534+
priceQuotes,
535+
instructions,
536+
enabledTransceivers,
537+
encodedNttManagerPayload
538+
);
539+
}
540+
541+
/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
542+
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view virtual;
543+
467544
/// ============== Invariants =============================================
468545

469546
/// @dev When we add new immutables, this function should be updated

evm/src/NttManager/MsgManager.sol

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity >=0.8.8 <0.9.0;
3+
4+
import "wormhole-solidity-sdk/Utils.sol";
5+
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
6+
7+
import "../interfaces/IMsgManager.sol";
8+
import "../interfaces/ITransceiver.sol";
9+
import "../libraries/TransceiverHelpers.sol";
10+
11+
import {ManagerBase} from "./ManagerBase.sol";
12+
13+
contract MsgManager is IMsgManager, ManagerBase {
14+
string public constant MSG_MANAGER_VERSION = "1.0.0";
15+
16+
// =============== Setup =================================================================
17+
18+
constructor(
19+
uint16 _chainId
20+
) ManagerBase(address(0), Mode.LOCKING, _chainId) {}
21+
22+
function _initialize() internal virtual override {
23+
_init();
24+
_checkThresholdInvariants();
25+
_checkTransceiversInvariants();
26+
}
27+
28+
function _init() internal onlyInitializing {
29+
// check if the owner is the deployer of this contract
30+
if (msg.sender != deployer) {
31+
revert UnexpectedDeployer(deployer, msg.sender);
32+
}
33+
if (msg.value != 0) {
34+
revert UnexpectedMsgValue();
35+
}
36+
__PausedOwnable_init(msg.sender, msg.sender);
37+
__ReentrancyGuard_init();
38+
}
39+
40+
// =============== Storage ==============================================================
41+
42+
bytes32 private constant PEERS_SLOT = bytes32(uint256(keccak256("mmgr.peers")) - 1);
43+
44+
// =============== Storage Getters/Setters ==============================================
45+
46+
function _getPeersStorage()
47+
internal
48+
pure
49+
returns (mapping(uint16 => MsgManagerPeer) storage $)
50+
{
51+
uint256 slot = uint256(PEERS_SLOT);
52+
assembly ("memory-safe") {
53+
$.slot := slot
54+
}
55+
}
56+
57+
// =============== Public Getters ========================================================
58+
59+
/// @inheritdoc IMsgManager
60+
function getPeer(
61+
uint16 chainId_
62+
) external view returns (MsgManagerPeer memory) {
63+
return _getPeersStorage()[chainId_];
64+
}
65+
66+
// =============== Admin ==============================================================
67+
68+
/// @inheritdoc IMsgManager
69+
function setPeer(uint16 peerChainId, bytes32 peerAddress) public onlyOwner {
70+
if (peerChainId == 0) {
71+
revert InvalidPeerChainIdZero();
72+
}
73+
if (peerAddress == bytes32(0)) {
74+
revert InvalidPeerZeroAddress();
75+
}
76+
if (peerChainId == chainId) {
77+
revert InvalidPeerSameChainId();
78+
}
79+
80+
MsgManagerPeer memory oldPeer = _getPeersStorage()[peerChainId];
81+
82+
_getPeersStorage()[peerChainId].peerAddress = peerAddress;
83+
84+
emit PeerUpdated(peerChainId, oldPeer.peerAddress, peerAddress);
85+
}
86+
87+
/// ============== Invariants =============================================
88+
89+
/// @dev When we add new immutables, this function should be updated
90+
function _checkImmutables() internal view virtual override {
91+
super._checkImmutables();
92+
}
93+
94+
// ==================== External Interface ===============================================
95+
96+
/// @inheritdoc IMsgManager
97+
function sendMessage(
98+
uint16 recipientChain,
99+
bytes calldata payload,
100+
bytes memory transceiverInstructions
101+
) external payable nonReentrant whenNotPaused returns (uint64 sequence) {
102+
sequence = _useMessageSequence();
103+
104+
bytes32 recipientAddress = _getPeersStorage()[recipientChain].peerAddress;
105+
106+
(uint256 totalPriceQuote, bytes memory encodedNttManagerPayload) = _sendMessage(
107+
recipientChain, recipientAddress, sequence, payload, transceiverInstructions
108+
);
109+
110+
emit MessageSent(
111+
recipientChain, recipientAddress, sequence, totalPriceQuote, encodedNttManagerPayload
112+
);
113+
}
114+
115+
/// @dev Override this function to handle your messages.
116+
function _handleMsg(
117+
uint16 sourceChainId,
118+
bytes32 sourceManagerAddress,
119+
TransceiverStructs.NttManagerMessage memory message,
120+
bytes32 digest
121+
) internal virtual override {}
122+
123+
// ==================== Internal Helpers ===============================================
124+
125+
/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
126+
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view override {
127+
if (_getPeersStorage()[sourceChainId].peerAddress != peerAddress) {
128+
revert InvalidPeer(sourceChainId, peerAddress);
129+
}
130+
}
131+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// SPDX-License-Identifier: Apache 2
2+
pragma solidity >=0.8.8 <0.9.0;
3+
4+
import "example-messaging-executor/evm/src/interfaces/IExecutor.sol";
5+
import "example-messaging-executor/evm/src/libraries/ExecutorMessages.sol";
6+
import "wormhole-solidity-sdk/Utils.sol";
7+
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
8+
9+
import "../interfaces/IMsgManagerWithExecutor.sol";
10+
import "../interfaces/ITransceiver.sol";
11+
import "../libraries/TransceiverHelpers.sol";
12+
13+
import {ManagerBase} from "./ManagerBase.sol";
14+
15+
contract MsgManagerWithExecutor is IMsgManagerWithExecutor, ManagerBase {
16+
string public constant MSG_MANAGER_VERSION = "1.0.0";
17+
18+
IExecutor public immutable executor;
19+
20+
// =============== Setup =================================================================
21+
22+
constructor(
23+
uint16 _chainId,
24+
address _executor
25+
) ManagerBase(address(0), Mode.LOCKING, _chainId) {
26+
assert(_executor != address(0));
27+
executor = IExecutor(_executor);
28+
}
29+
30+
function _initialize() internal virtual override {
31+
_init();
32+
_checkThresholdInvariants();
33+
_checkTransceiversInvariants();
34+
}
35+
36+
function _init() internal onlyInitializing {
37+
// check if the owner is the deployer of this contract
38+
if (msg.sender != deployer) {
39+
revert UnexpectedDeployer(deployer, msg.sender);
40+
}
41+
if (msg.value != 0) {
42+
revert UnexpectedMsgValue();
43+
}
44+
__PausedOwnable_init(msg.sender, msg.sender);
45+
__ReentrancyGuard_init();
46+
}
47+
48+
// =============== Storage ==============================================================
49+
50+
bytes32 private constant PEERS_SLOT = bytes32(uint256(keccak256("mmgr.peers")) - 1);
51+
52+
// =============== Storage Getters/Setters ==============================================
53+
54+
function _getPeersStorage()
55+
internal
56+
pure
57+
returns (mapping(uint16 => MsgManagerPeer) storage $)
58+
{
59+
uint256 slot = uint256(PEERS_SLOT);
60+
assembly ("memory-safe") {
61+
$.slot := slot
62+
}
63+
}
64+
65+
// =============== Public Getters ========================================================
66+
67+
/// @inheritdoc IMsgManagerWithExecutor
68+
function getPeer(
69+
uint16 chainId_
70+
) external view returns (MsgManagerPeer memory) {
71+
return _getPeersStorage()[chainId_];
72+
}
73+
74+
// =============== Admin ==============================================================
75+
76+
/// @inheritdoc IMsgManagerWithExecutor
77+
function setPeer(uint16 peerChainId, bytes32 peerAddress) public onlyOwner {
78+
if (peerChainId == 0) {
79+
revert InvalidPeerChainIdZero();
80+
}
81+
if (peerAddress == bytes32(0)) {
82+
revert InvalidPeerZeroAddress();
83+
}
84+
if (peerChainId == chainId) {
85+
revert InvalidPeerSameChainId();
86+
}
87+
88+
MsgManagerPeer memory oldPeer = _getPeersStorage()[peerChainId];
89+
90+
_getPeersStorage()[peerChainId].peerAddress = peerAddress;
91+
92+
emit PeerUpdated(peerChainId, oldPeer.peerAddress, peerAddress);
93+
}
94+
95+
/// ============== Invariants =============================================
96+
97+
/// @dev When we add new immutables, this function should be updated
98+
function _checkImmutables() internal view virtual override {
99+
super._checkImmutables();
100+
}
101+
102+
// ==================== External Interface ===============================================
103+
104+
/// @inheritdoc IMsgManagerWithExecutor
105+
function sendMessage(
106+
uint16 recipientChain,
107+
bytes calldata payload,
108+
bytes memory transceiverInstructions,
109+
ExecutorArgs calldata executorArgs
110+
) external payable nonReentrant whenNotPaused returns (uint64 sequence) {
111+
sequence = _useMessageSequence();
112+
113+
bytes32 recipientAddress = _getPeersStorage()[recipientChain].peerAddress;
114+
115+
(uint256 totalPriceQuote,) = _sendMessage(
116+
recipientChain, recipientAddress, sequence, payload, transceiverInstructions
117+
);
118+
119+
if (totalPriceQuote + executorArgs.value > msg.value) {
120+
revert InsufficientMsgValue(msg.value, totalPriceQuote, executorArgs.value);
121+
}
122+
123+
// emit MessageSent(recipientChain, recipientAddress, sequence, totalPriceQuote);
124+
125+
// Generate the executor event.
126+
// TODO: Not sure we want to use `makeNTTv1Request` since it doesn't have the payload.
127+
executor.requestExecution{value: executorArgs.value}(
128+
recipientChain,
129+
recipientAddress,
130+
executorArgs.refundAddress,
131+
executorArgs.signedQuote,
132+
ExecutorMessages.makeNTTv1Request(
133+
chainId, bytes32(uint256(uint160(address(this)))), bytes32(uint256(sequence))
134+
),
135+
executorArgs.instructions
136+
);
137+
}
138+
139+
/// @dev Override this function to handle your messages.
140+
function _handleMsg(
141+
uint16 sourceChainId,
142+
bytes32 sourceManagerAddress,
143+
TransceiverStructs.NttManagerMessage memory message,
144+
bytes32 digest
145+
) internal virtual override {}
146+
147+
// ==================== Internal Helpers ===============================================
148+
149+
/// @dev Verify that the peer address saved for `sourceChainId` matches the `peerAddress`.
150+
function _verifyPeer(uint16 sourceChainId, bytes32 peerAddress) internal view override {
151+
if (_getPeersStorage()[sourceChainId].peerAddress != peerAddress) {
152+
revert InvalidPeer(sourceChainId, peerAddress);
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)