@@ -23,13 +23,16 @@ import {IMintBurnToken} from "../interfaces/IMintBurnToken.sol";
23
23
import {BurnMessageV2} from "../messages/v2/BurnMessageV2.sol " ;
24
24
import {AddressUtils} from "../messages/v2/AddressUtils.sol " ;
25
25
import {MessageTransmitterV2} from "./MessageTransmitterV2.sol " ;
26
+ import {IMessageHandlerV2} from "../interfaces/v2/IMessageHandlerV2.sol " ;
27
+ import {TypedMemView} from "@memview-sol/contracts/TypedMemView.sol " ;
28
+ import {BurnMessageV2} from "../messages/v2/BurnMessageV2.sol " ;
26
29
27
30
/**
28
31
* @title TokenMessengerV2
29
32
* @notice Sends messages and receives messages to/from MessageTransmitters
30
33
* and to/from TokenMinters
31
34
*/
32
- contract TokenMessengerV2 is BaseTokenMessenger {
35
+ contract TokenMessengerV2 is IMessageHandlerV2 , BaseTokenMessenger {
33
36
// ============ Events ============
34
37
/**
35
38
* @notice Emitted when a DepositForBurn message is sent
@@ -43,7 +46,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
43
46
* If equal to bytes32(0), any address can broadcast the message.
44
47
* @param maxFee maximum fee to pay on destination domain, in units of burnToken
45
48
* @param minFinalityThreshold the minimum finality at which the message should be attested to.
46
- * @param hook target and calldata for execution on destination domain
49
+ * @param hookData optional hook for execution on destination domain
47
50
*/
48
51
event DepositForBurn (
49
52
address indexed burnToken ,
@@ -55,13 +58,15 @@ contract TokenMessengerV2 is BaseTokenMessenger {
55
58
bytes32 destinationCaller ,
56
59
uint256 maxFee ,
57
60
uint32 indexed minFinalityThreshold ,
58
- bytes hook
61
+ bytes hookData
59
62
);
60
63
61
64
// ============ Libraries ============
65
+ using TypedMemView for bytes ;
66
+ using TypedMemView for bytes29 ;
67
+ using BurnMessageV2 for bytes29 ;
62
68
63
69
// ============ State Variables ============
64
- uint32 public immutable MIN_HOOK_LENGTH = 32 ;
65
70
66
71
// ============ Modifiers ============
67
72
@@ -105,7 +110,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
105
110
uint256 maxFee ,
106
111
uint32 minFinalityThreshold
107
112
) external {
108
- bytes calldata _emptyHook = msg .data [0 :0 ];
113
+ bytes calldata _emptyHookData = msg .data [0 :0 ];
109
114
_depositForBurn (
110
115
amount,
111
116
destinationDomain,
@@ -114,25 +119,22 @@ contract TokenMessengerV2 is BaseTokenMessenger {
114
119
destinationCaller,
115
120
maxFee,
116
121
minFinalityThreshold,
117
- _emptyHook
122
+ _emptyHookData
118
123
);
119
124
}
120
125
121
126
/**
122
127
* @notice Deposits and burns tokens from sender to be minted on destination domain.
123
128
* Emits a `DepositForBurn` event.
124
129
* @dev reverts if:
125
- * - hook appears invalid, such as being less than 32 bytes in length
130
+ * - hookData is zero- length
126
131
* - given burnToken is not supported
127
132
* - given destinationDomain has no TokenMessenger registered
128
133
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
129
134
* to this contract is less than `amount`.
130
135
* - burn() reverts. For example, if `amount` is 0.
131
136
* - fee is greater than or equal to the amount.
132
137
* - MessageTransmitter#sendMessage reverts.
133
- * @dev Note that even if the hook reverts on the destination domain, the mint will still proceed.
134
- * @dev Hook formatting:
135
- * - TODO: STABLE-7280
136
138
* @param amount amount of tokens to burn
137
139
* @param destinationDomain destination domain
138
140
* @param mintRecipient address of mint recipient on destination domain
@@ -141,7 +143,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
141
143
* any address can broadcast the message.
142
144
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
143
145
* @param minFinalityThreshold the minimum finality at which a burn message will be attested to.
144
- * @param hook hook to execute on destination domain. Must be 32-bytes length or more.
146
+ * @param hookData hook data to append to burn message for interpretation on destination domain
145
147
*/
146
148
function depositForBurnWithHook (
147
149
uint256 amount ,
@@ -151,9 +153,9 @@ contract TokenMessengerV2 is BaseTokenMessenger {
151
153
bytes32 destinationCaller ,
152
154
uint256 maxFee ,
153
155
uint32 minFinalityThreshold ,
154
- bytes calldata hook
156
+ bytes calldata hookData
155
157
) external {
156
- require (hook .length >= MIN_HOOK_LENGTH , "Invalid hook length " );
158
+ require (hookData .length > 0 , "Hook data is empty " );
157
159
158
160
_depositForBurn (
159
161
amount,
@@ -163,11 +165,78 @@ contract TokenMessengerV2 is BaseTokenMessenger {
163
165
destinationCaller,
164
166
maxFee,
165
167
minFinalityThreshold,
166
- hook
168
+ hookData
169
+ );
170
+ }
171
+
172
+ /**
173
+ * @notice Handles an incoming finalized message received by the local MessageTransmitter,
174
+ * and takes the appropriate action. For a burn message, mints the
175
+ * associated token to the requested recipient on the local domain.
176
+ * @dev Validates the local sender is the local MessageTransmitter, and the
177
+ * remote sender is a registered remote TokenMessenger for `remoteDomain`.
178
+ * @param remoteDomain The domain where the message originated from.
179
+ * @param sender The sender of the message (remote TokenMessenger).
180
+ * @param messageBody The message body bytes.
181
+ * @return success Bool, true if successful.
182
+ */
183
+ function handleReceiveFinalizedMessage (
184
+ uint32 remoteDomain ,
185
+ bytes32 sender ,
186
+ uint32 ,
187
+ bytes calldata messageBody
188
+ )
189
+ external
190
+ override
191
+ onlyLocalMessageTransmitter
192
+ onlyRemoteTokenMessenger (remoteDomain, sender)
193
+ returns (bool )
194
+ {
195
+ bytes29 _msg = messageBody.ref (0 );
196
+ _msg._validateBurnMessageFormat ();
197
+ require (
198
+ _msg._getVersion () == messageBodyVersion,
199
+ "Invalid message body version "
167
200
);
201
+
202
+ bytes32 _mintRecipient = _msg._getMintRecipient ();
203
+ bytes32 _burnToken = _msg._getBurnToken ();
204
+ uint256 _amount = _msg._getAmount ();
205
+
206
+ ITokenMinter _localMinter = _getLocalMinter ();
207
+
208
+ _mintAndWithdraw (
209
+ address (_localMinter),
210
+ remoteDomain,
211
+ _burnToken,
212
+ AddressUtils.bytes32ToAddress (_mintRecipient),
213
+ _amount
214
+ );
215
+
216
+ return true ;
217
+ }
218
+
219
+ function handleReceiveUnfinalizedMessage (
220
+ uint32 sourceDomain ,
221
+ bytes32 sender ,
222
+ uint32 finalityThresholdExecuted ,
223
+ bytes calldata messageBody
224
+ ) external override returns (bool ) {
225
+ // TODO: STABLE-7292
168
226
}
169
227
170
228
// ============ Internal Utils ============
229
+ /**
230
+ * @notice Deposits and burns tokens from sender to be minted on destination domain.
231
+ * Emits a `DepositForBurn` event.
232
+ * @param _amount amount of tokens to burn (must be non-zero)
233
+ * @param _destinationDomain destination domain
234
+ * @param _mintRecipient address of mint recipient on destination domain
235
+ * @param _burnToken address of contract to burn deposited tokens, on local domain
236
+ * @param _destinationCaller caller on the destination domain, as bytes32
237
+ * @param _maxFee maximum fee to pay on destination chain
238
+ * @param _hookData optional hook data for execution on destination chain
239
+ */
171
240
function _depositForBurn (
172
241
uint256 _amount ,
173
242
uint32 _destinationDomain ,
@@ -176,7 +245,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
176
245
bytes32 _destinationCaller ,
177
246
uint256 _maxFee ,
178
247
uint32 _minFinalityThreshold ,
179
- bytes calldata _hook
248
+ bytes calldata _hookData
180
249
) internal {
181
250
require (_amount > 0 , "Amount must be nonzero " );
182
251
require (_mintRecipient != bytes32 (0 ), "Mint recipient must be nonzero " );
@@ -187,7 +256,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
187
256
);
188
257
189
258
// Deposit and burn tokens
190
- _depositAndBurnTokens (_burnToken, msg .sender , _amount);
259
+ _depositAndBurn (_burnToken, msg .sender , _amount);
191
260
192
261
// Format message body
193
262
bytes memory _burnMessage = BurnMessageV2._formatMessageForRelay (
@@ -197,7 +266,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
197
266
_amount,
198
267
AddressUtils.addressToBytes32 (msg .sender ),
199
268
_maxFee,
200
- _hook
269
+ _hookData
201
270
);
202
271
203
272
// Send message
@@ -219,21 +288,7 @@ contract TokenMessengerV2 is BaseTokenMessenger {
219
288
_destinationCaller,
220
289
_maxFee,
221
290
_minFinalityThreshold,
222
- _hook
223
- );
224
- }
225
-
226
- function _depositAndBurnTokens (
227
- address _burnToken ,
228
- address _from ,
229
- uint256 _amount
230
- ) internal {
231
- ITokenMinter _localMinter = _getLocalMinter ();
232
- IMintBurnToken _mintBurnToken = IMintBurnToken (_burnToken);
233
- require (
234
- _mintBurnToken.transferFrom (_from, address (_localMinter), _amount),
235
- "Transfer operation failed "
291
+ _hookData
236
292
);
237
- _localMinter.burn (_burnToken, _amount);
238
293
}
239
294
}
0 commit comments