@@ -11,6 +11,7 @@ import {PackageVersioned} from "../../PackageVersioned.sol";
1111import {IWETH} from "../interfaces/IWETH.sol " ;
1212import {TokenMessage} from "../libs/TokenMessage.sol " ;
1313import {TypeCasts} from "../../libs/TypeCasts.sol " ;
14+ import {FungibleTokenRouter} from "../libs/FungibleTokenRouter.sol " ;
1415
1516/**
1617 * @notice Information about an output asset for a destination domain
@@ -68,6 +69,9 @@ contract EverclearTokenBridge is HypERC20Collateral {
6869
6970 /**
7071 * @notice Constructor to initialize the Everclear token bridge
72+ * @param _erc20 The address of the ERC20 token to be bridged
73+ * @param _scale The scaling factor for token amounts (typically 1 for 18-decimal tokens)
74+ * @param _mailbox The address of the Hyperlane mailbox contract
7175 * @param _everclearAdapter The address of the Everclear adapter contract
7276 */
7377 constructor (
@@ -81,8 +85,10 @@ contract EverclearTokenBridge is HypERC20Collateral {
8185 }
8286
8387 /**
84- * @notice Initializes the proxy contract.
85- * @dev Approves the Everclear adapter to spend tokens
88+ * @notice Initializes the proxy contract
89+ * @dev Approves the Everclear adapter to spend tokens and calls parent initialization
90+ * @param _hook The address of the post-dispatch hook (can be zero address)
91+ * @param _owner The address that will own this contract
8692 */
8793 function initialize (address _hook , address _owner ) public initializer {
8894 _HypERC20_initialize (_hook, address (0 ), _owner);
@@ -109,6 +115,11 @@ contract EverclearTokenBridge is HypERC20Collateral {
109115 emit FeeParamsUpdated (_fee, _deadline);
110116 }
111117
118+ /**
119+ * @notice Internal function to set the output asset for a destination domain
120+ * @dev Emits OutputAssetSet event when successful
121+ * @param _outputAssetInfo The output asset information containing destination and asset address
122+ */
112123 function _setOutputAsset (
113124 OutputAssetInfo calldata _outputAssetInfo
114125 ) internal {
@@ -172,27 +183,13 @@ contract EverclearTokenBridge is HypERC20Collateral {
172183 });
173184 }
174185
175- /// @dev We can't use _feeAmount here because Everclear wants to pull tokens from this contract
176- /// and the amount from _feeAmount is sent to the fee recipient.
177- function _chargeSender (
178- uint32 _destination ,
179- bytes32 _recipient ,
180- uint256 _amount
181- ) internal virtual override returns (uint256 dispatchValue ) {
182- return
183- super ._chargeSender (
184- _destination,
185- _recipient,
186- _amount + feeParams.fee
187- );
188- }
189-
190186 /**
191187 * @notice Creates an Everclear intent for cross-chain token transfer
192188 * @dev Internal function to handle intent creation with Everclear adapter
193189 * @param _destination The destination domain ID
194190 * @param _recipient The recipient address on the destination chain
195191 * @param _amount The amount of tokens to transfer
192+ * @return The created Everclear intent struct containing all transfer details
196193 */
197194 function _createIntent (
198195 uint32 _destination ,
@@ -209,40 +206,72 @@ contract EverclearTokenBridge is HypERC20Collateral {
209206 destinations[0 ] = _destination;
210207
211208 // Create intent
212- // We always send the funds to the remote router, which will then send them to the recipient in _handle
213209 (, IEverclear.Intent memory intent ) = everclearAdapter.newIntent ({
214210 _destinations: destinations,
215- _receiver: _mustHaveRemoteRouter (_destination),
211+ _receiver: _getReceiver (_destination, _recipient ),
216212 _inputAsset: address (wrappedToken),
217213 _outputAsset: outputAssets[_destination], // We load this from storage again to avoid stack too deep
218214 _amount: _amount,
219215 _maxFee: 0 ,
220216 _ttl: 0 ,
221- _data: _getIntentCalldata (_recipient, _amount) ,
217+ _data: "" ,
222218 _feeParams: feeParams
223219 });
224220
225221 return intent;
226222 }
227223
228224 /**
229- * @notice Gets the calldata for the intent that will unwrap WETH to ETH on destination
230- * @dev Overrides parent to return calldata for unwrapping WETH to ETH
231- * @return The encoded calldata for the unwrap and send operation
225+ * @notice Gets the receiver address for an intent
226+ * @dev Virtual function that can be overridden by derived contracts
227+ * @param _destination The destination domain ID
228+ * @param _recipient The intended recipient address
229+ * @return receiver The receiver address to use in the intent (typically the recipient for token bridge)
232230 */
233- function _getIntentCalldata (
231+ function _getReceiver (
232+ uint32 _destination ,
233+ bytes32 _recipient
234+ ) internal view virtual returns (bytes32 ) {
235+ return _recipient;
236+ }
237+
238+ /**
239+ * @notice Charges the sender for the transfer including Everclear fees
240+ * @dev We can't use _feeAmount here because Everclear wants to pull tokens from this contract
241+ * and the amount from _feeAmount is sent to the fee recipient.
242+ * @param _destination The destination domain ID
243+ * @param _recipient The recipient address on the destination chain
244+ * @param _amount The amount of tokens to transfer (excluding fees)
245+ * @return dispatchValue The ETH value to include with the Hyperlane message dispatch
246+ */
247+ function _chargeSender (
248+ uint32 _destination ,
234249 bytes32 _recipient ,
235250 uint256 _amount
236- ) internal view returns (bytes memory ) {
237- return abi.encode (_recipient, _amount);
251+ ) internal virtual override returns (uint256 dispatchValue ) {
252+ return
253+ super ._chargeSender (
254+ _destination,
255+ _recipient,
256+ _amount + feeParams.fee
257+ );
238258 }
239259
260+ /**
261+ * @notice Handles pre-dispatch logic including charging sender and creating Everclear intent
262+ * @dev Overrides parent function to integrate with Everclear's intent system
263+ * @param _destination The destination domain ID
264+ * @param _recipient The recipient address on the destination chain
265+ * @param _amount The amount of tokens to transfer
266+ * @return dispatchValue The ETH value to include with the message dispatch
267+ * @return message The encoded message containing transfer details and intent
268+ */
240269 function _beforeDispatch (
241270 uint32 _destination ,
242271 bytes32 _recipient ,
243272 uint256 _amount
244273 ) internal virtual override returns (uint256 , bytes memory ) {
245- ( uint256 _dispatchValue , bytes memory _msg ) = super . _beforeDispatch (
274+ uint256 dispatchValue = _chargeSender (
246275 _destination,
247276 _recipient,
248277 _amount
@@ -254,45 +283,51 @@ contract EverclearTokenBridge is HypERC20Collateral {
254283 _amount
255284 );
256285
257- // Add the intent to the `TokenMessage` as metadata
258- // The original `_msg` is abi.encodePacked(_recipient, _amount)
259- // We need can't use abi.encodePacked because the intent is a struct
260- _msg = bytes .concat (_msg, abi.encode (intent));
286+ bytes memory message = TokenMessage.format (
287+ _recipient,
288+ _outboundAmount (_amount),
289+ abi.encode (intent)
290+ );
261291
262- return (_dispatchValue, _msg );
292+ return (dispatchValue, message );
263293 }
264294
295+ /**
296+ * @dev No-op, the funds are transferred directly to `_recipient` via Everclear
297+ * @param _recipient The address to receive the tokens
298+ * @param _amount The amount of tokens to transfer
299+ */
300+ function _transferTo (
301+ address _recipient ,
302+ uint256 _amount
303+ ) internal virtual override {
304+ // No-op, the funds are transferred directly to `_recipient` via Everclear
305+ }
306+
307+ /**
308+ * @notice Handles incoming messages from remote chains
309+ * @dev For the base token bridge, this is a no-op since funds are transferred via Everclear
310+ * @param _origin The origin domain ID where the message was sent from
311+ * @param _message The message payload (unused in base implementation)
312+ */
265313 function _handle (
266314 uint32 _origin ,
267- bytes32 /* sender */ ,
315+ bytes32 _sender ,
268316 bytes calldata _message
269317 ) internal virtual override {
270318 // Get intent from hyperlane message
271319 bytes memory metadata = _message.metadata ();
272- IEverclear.Intent memory intent = abi.decode (
273- metadata,
274- (IEverclear.Intent)
275- );
320+ bytes32 intentId = keccak256 (metadata);
276321
277- /* CHECKS */
278- // Check that intent is settled
279- bytes32 intentId = keccak256 (abi.encode (intent));
322+ // Check Everclear intent status
280323 require (
281324 everclearSpoke.status (intentId) == IEverclear.IntentStatus.SETTLED,
282325 "ETB: Intent Status != SETTLED "
283326 );
284327 // Check that we have not processed this intent before
285328 require (! intentSettled[intentId], "ETB: Intent already processed " );
286- (bytes32 _recipient , uint256 _amount ) = abi.decode (
287- intent.data,
288- (bytes32 , uint256 )
289- );
290329
291- /* EFFECTS */
292330 intentSettled[intentId] = true ;
293- emit ReceivedTransferRemote (_origin, _recipient, _amount);
294-
295- /* INTERACTIONS */
296- _transferTo (_recipient.bytes32ToAddress (), _amount);
331+ super ._handle (_origin, _sender, _message);
297332 }
298333}
0 commit comments