|
| 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