@@ -56,25 +56,14 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
5656 constructor (address _vault , address _assetOFT , address _shareOFT ) {
5757 VAULT = IERC4626 (_vault);
5858
59- ASSET_OFT = _assetOFT;
6059 SHARE_OFT = _shareOFT;
61- SHARE_ERC20 = IOFT (SHARE_OFT).token ();
60+ ASSET_OFT = _assetOFT;
61+
62+ SHARE_ERC20 = _initializeShareToken ();
63+ ASSET_ERC20 = _initializeAssetToken ();
6264
6365 ENDPOINT = address (IOAppCore (ASSET_OFT).endpoint ());
6466 VAULT_EID = ILayerZeroEndpointV2 (ENDPOINT).eid ();
65-
66- if (SHARE_ERC20 != address (VAULT)) {
67- revert ShareTokenNotVault (SHARE_ERC20, address (VAULT));
68- }
69-
70- /// @dev ShareOFT must be an OFT adapter. We can infer this by checking 'approvalRequired()'.
71- /// @dev burn() on tokens when a user sends changes totalSupply() which the asset:share ratio depends on.
72- if (! IOFT (SHARE_OFT).approvalRequired ()) revert ShareOFTNotAdapter (SHARE_OFT);
73-
74- /// @dev Approve the share adapter with the share tokens held by this contract
75- IERC20 (SHARE_ERC20).approve (_shareOFT, type (uint256 ).max);
76-
77- ASSET_ERC20 = _initializeAssetToken (_assetOFT, VAULT);
7867 }
7968
8069 /**
@@ -111,7 +100,7 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
111100 }
112101 }
113102
114- _refund (_composeSender, _message, amount, tx .origin );
103+ _refund (_composeSender, _message, amount, tx .origin , msg . value );
115104 emit Refunded (_guid);
116105 }
117106 }
@@ -141,9 +130,9 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
141130 if (msg .value < minMsgValue) revert InsufficientMsgValue (minMsgValue, msg .value );
142131
143132 if (_oftIn == ASSET_OFT) {
144- _depositAndSend (_composeFrom, _amount, sendParam, tx .origin );
133+ _depositAndSend (_composeFrom, _amount, sendParam, tx .origin , msg . value );
145134 } else {
146- _redeemAndSend (_composeFrom, _amount, sendParam, tx .origin );
135+ _redeemAndSend (_composeFrom, _amount, sendParam, tx .origin , msg . value );
147136 }
148137 }
149138
@@ -159,7 +148,13 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
159148 address _refundAddress
160149 ) external payable virtual nonReentrant {
161150 IERC20 (ASSET_ERC20).safeTransferFrom (msg .sender , address (this ), _assetAmount);
162- _depositAndSend (OFTComposeMsgCodec.addressToBytes32 (msg .sender ), _assetAmount, _sendParam, _refundAddress);
151+ _depositAndSend (
152+ OFTComposeMsgCodec.addressToBytes32 (msg .sender ),
153+ _assetAmount,
154+ _sendParam,
155+ _refundAddress,
156+ msg .value
157+ );
163158 }
164159
165160 /**
@@ -168,6 +163,7 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
168163 * @param _assetAmount The number of assets to deposit
169164 * @param _sendParam Parameter that defines how to send the shares
170165 * @param _refundAddress Address to receive excess payment of the LZ fees
166+ * @param _msgValue The amount of native tokens sent with the transaction
171167 * @notice This function first deposits the assets to mint shares, validates the shares meet minimum slippage requirements,
172168 * then sends the minted shares cross-chain using the OFT (Omnichain Fungible Token) protocol
173169 * @notice _sendParam.amountLD is set to the share amount minted, and minAmountLD is reset to 0 for send operation
@@ -176,15 +172,16 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
176172 bytes32 _depositor ,
177173 uint256 _assetAmount ,
178174 SendParam memory _sendParam ,
179- address _refundAddress
175+ address _refundAddress ,
176+ uint256 _msgValue
180177 ) internal virtual {
181178 uint256 shareAmount = _deposit (_depositor, _assetAmount);
182179 _assertSlippage (shareAmount, _sendParam.minAmountLD);
183180
184181 _sendParam.amountLD = shareAmount;
185182 _sendParam.minAmountLD = 0 ;
186183
187- _send (SHARE_OFT, _sendParam, _refundAddress);
184+ _send (SHARE_OFT, _sendParam, _refundAddress, _msgValue );
188185 emit Deposited (_depositor, _sendParam.to, _sendParam.dstEid, _assetAmount, shareAmount);
189186 }
190187
@@ -210,7 +207,13 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
210207 address _refundAddress
211208 ) external payable virtual nonReentrant {
212209 IERC20 (SHARE_ERC20).safeTransferFrom (msg .sender , address (this ), _shareAmount);
213- _redeemAndSend (OFTComposeMsgCodec.addressToBytes32 (msg .sender ), _shareAmount, _sendParam, _refundAddress);
210+ _redeemAndSend (
211+ OFTComposeMsgCodec.addressToBytes32 (msg .sender ),
212+ _shareAmount,
213+ _sendParam,
214+ _refundAddress,
215+ msg .value
216+ );
214217 }
215218
216219 /**
@@ -219,6 +222,7 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
219222 * @param _shareAmount The number of shares to redeem
220223 * @param _sendParam Parameter that defines how to send the assets
221224 * @param _refundAddress Address to receive excess payment of the LZ fees
225+ * @param _msgValue The amount of native tokens sent with the transaction
222226 * @notice This function first redeems the specified share amount for the underlying asset,
223227 * validates the received amount against slippage protection, then initiates a cross-chain
224228 * transfer of the redeemed assets using the OFT (Omnichain Fungible Token) protocol
@@ -229,15 +233,16 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
229233 bytes32 _redeemer ,
230234 uint256 _shareAmount ,
231235 SendParam memory _sendParam ,
232- address _refundAddress
236+ address _refundAddress ,
237+ uint256 _msgValue
233238 ) internal virtual {
234239 uint256 assetAmount = _redeem (_redeemer, _shareAmount);
235240 _assertSlippage (assetAmount, _sendParam.minAmountLD);
236241
237242 _sendParam.amountLD = assetAmount;
238243 _sendParam.minAmountLD = 0 ;
239244
240- _send (ASSET_OFT, _sendParam, _refundAddress);
245+ _send (ASSET_OFT, _sendParam, _refundAddress, _msgValue );
241246 emit Redeemed (_redeemer, _sendParam.to, _sendParam.dstEid, _shareAmount, assetAmount);
242247 }
243248
@@ -303,12 +308,13 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
303308 * @param _oft The OFT contract address to use for sending
304309 * @param _sendParam The parameters for the send operation
305310 * @param _refundAddress Address to receive excess payment of the LZ fees
311+ * @param _msgValue The amount of native tokens sent with the transaction
306312 */
307- function _send (address _oft , SendParam memory _sendParam , address _refundAddress ) internal virtual {
313+ function _send (address _oft , SendParam memory _sendParam , address _refundAddress , uint256 _msgValue ) internal virtual {
308314 if (_sendParam.dstEid == VAULT_EID) {
309- _sendLocal (_oft, _sendParam, _refundAddress);
315+ _sendLocal (_oft, _sendParam, _refundAddress, _msgValue );
310316 } else {
311- _sendRemote (_oft, _sendParam, _refundAddress);
317+ _sendRemote (_oft, _sendParam, _refundAddress, _msgValue );
312318 }
313319 }
314320
@@ -317,9 +323,11 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
317323 * @dev Transfers tokens directly without LayerZero messaging
318324 * @param _oft The OFT contract address to determine which token to transfer
319325 * @param _sendParam The parameters for the send operation
326+ * @dev _refundAddress Address to receive excess payment of the LZ fees (unused for local transfers)
327+ * @param _msgValue The amount of native tokens sent with the transaction (must be 0 for local transfers)
320328 */
321- function _sendLocal (address _oft , SendParam memory _sendParam , address /*_refundAddress*/ ) internal virtual {
322- if (msg . value > 0 ) revert NoMsgValueExpected ();
329+ function _sendLocal (address _oft , SendParam memory _sendParam , address /*_refundAddress*/ , uint256 _msgValue ) internal virtual {
330+ if (_msgValue > 0 ) revert NoMsgValueExpected ();
323331
324332 /// @dev Can do this because _oft is validated before this function is called
325333 address erc20 = _oft == ASSET_OFT ? ASSET_ERC20 : SHARE_ERC20;
@@ -332,9 +340,10 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
332340 * @param _oft The OFT contract address to use for sending
333341 * @param _sendParam The parameters for the send operation
334342 * @param _refundAddress Address to receive excess payment of the LZ fees
343+ * @param _msgValue The amount of native tokens sent with the transaction
335344 */
336- function _sendRemote (address _oft , SendParam memory _sendParam , address _refundAddress ) internal virtual {
337- IOFT (_oft).send { value: msg . value }(_sendParam, MessagingFee (msg . value , 0 ), _refundAddress);
345+ function _sendRemote (address _oft , SendParam memory _sendParam , address _refundAddress , uint256 _msgValue ) internal virtual {
346+ IOFT (_oft).send { value: _msgValue }(_sendParam, MessagingFee (_msgValue , 0 ), _refundAddress);
338347 }
339348
340349 /**
@@ -343,36 +352,59 @@ contract VaultComposerSync is IVaultComposerSync, ReentrancyGuard {
343352 * @param _message The original message that was sent
344353 * @param _amount The amount of tokens to refund
345354 * @param _refundAddress Address to receive the refund
355+ * @param _msgValue The amount of native tokens sent with the transaction
346356 */
347- function _refund (address _oft , bytes calldata _message , uint256 _amount , address _refundAddress ) internal virtual {
357+ function _refund (address _oft , bytes calldata _message , uint256 _amount , address _refundAddress , uint256 _msgValue ) internal virtual {
348358 /// @dev Extracted from the _message header. Will always be part of the _message since it is created by lzReceive
349359 SendParam memory refundSendParam;
350360 refundSendParam.dstEid = OFTComposeMsgCodec.srcEid (_message);
351361 refundSendParam.to = OFTComposeMsgCodec.composeFrom (_message);
352362 refundSendParam.amountLD = _amount;
353363
354- _sendRemote (_oft, refundSendParam, _refundAddress);
364+ _sendRemote (_oft, refundSendParam, _refundAddress, _msgValue);
365+ }
366+
367+ /**
368+ * @dev Internal function to validate the share token compatibility
369+ * @dev Validate part of the constructor in an overridable function due to differences in asset and OFT token
370+ * @return shareERC20 The address of the share ERC20 token
371+ * @notice Share token must be the vault itself
372+ * @notice Share OFT must be an adapter (approvalRequired() returns true)
373+ */
374+ function _initializeShareToken () internal virtual returns (address shareERC20 ) {
375+ shareERC20 = IOFT (SHARE_OFT).token ();
376+
377+ if (shareERC20 != address (VAULT)) {
378+ revert ShareTokenNotVault (shareERC20, address (VAULT));
379+ }
380+
381+ /// @dev ShareOFT must be an OFT adapter. We can infer this by checking 'approvalRequired()'.
382+ /// @dev burn() on tokens when a user sends changes totalSupply() which the asset:share ratio depends on.
383+ if (! IOFT (SHARE_OFT).approvalRequired ()) revert ShareOFTNotAdapter (SHARE_OFT);
384+
385+ /// @dev Approve the share adapter with the share tokens held by this contract
386+ IERC20 (shareERC20).approve (SHARE_OFT, type (uint256 ).max);
355387 }
356388
357389 /**
358390 * @dev Internal function to validate the asset token compatibility
359391 * @dev Validate part of the constructor in an overridable function due to differences in asset and OFT token
360392 * @dev For example, in the case of VaultComposerSyncPoolNative, the asset token is WETH but the OFT token is native
361- * @param _assetOFT The address of the asset OFT
362- * @param _vault The address of the vault
393+ * @return assetERC20 The address of the asset ERC20 token
394+ * @notice Asset token should match the vault's underlying asset (overridable behavior)
363395 */
364- function _initializeAssetToken (address _assetOFT , IERC4626 _vault ) internal virtual returns (address assetERC20 ) {
365- assetERC20 = IOFT (_assetOFT ).token ();
396+ function _initializeAssetToken () internal virtual returns (address assetERC20 ) {
397+ assetERC20 = IOFT (ASSET_OFT ).token ();
366398
367- if (assetERC20 != address (_vault .asset ())) {
368- revert AssetTokenNotVaultAsset (assetERC20, address (_vault .asset ()));
399+ if (assetERC20 != address (VAULT .asset ())) {
400+ revert AssetTokenNotVaultAsset (assetERC20, address (VAULT .asset ()));
369401 }
370402
371- /// @dev Approve the vault to spend the asset tokens held by this contract
372- IERC20 (assetERC20).approve (address (_vault), type (uint256 ).max);
373-
374403 /// @dev If the asset OFT is an adapter, approve it as well
375- if (IOFT (_assetOFT).approvalRequired ()) IERC20 (assetERC20).approve (_assetOFT, type (uint256 ).max);
404+ if (IOFT (ASSET_OFT).approvalRequired ()) IERC20 (assetERC20).approve (ASSET_OFT, type (uint256 ).max);
405+
406+ /// @dev Approve the vault to spend the asset tokens held by this contract
407+ IERC20 (assetERC20).approve (address (VAULT), type (uint256 ).max);
376408 }
377409
378410 receive () external payable virtual {}
0 commit comments