diff --git a/contracts/interfaces/IBridge.sol b/contracts/interfaces/IBridge.sol index 76e36b136..a71245e29 100644 --- a/contracts/interfaces/IBridge.sol +++ b/contracts/interfaces/IBridge.sol @@ -3,6 +3,8 @@ pragma solidity >=0.8.0; interface IBridge { + function nativeWrap() external view returns (address); + function send( address _receiver, address _token, diff --git a/contracts/message/apps/BatchTransfer.sol b/contracts/message/apps/BatchTransfer.sol index 4f8b2bcaf..ad1aba716 100644 --- a/contracts/message/apps/BatchTransfer.sol +++ b/contracts/message/apps/BatchTransfer.sol @@ -123,6 +123,7 @@ contract BatchTransfer is MessageSenderApp, MessageReceiverApp { address // executor ) external payable override onlyMessageBus returns (ExecutionStatus) { TransferRequest memory transfer = abi.decode((_message), (TransferRequest)); + wrapBridgeOutToken(_token, _amount); uint256 totalAmt; for (uint256 i = 0; i < transfer.accounts.length; i++) { IERC20(_token).safeTransfer(transfer.accounts[i], transfer.amounts[i]); @@ -150,6 +151,7 @@ contract BatchTransfer is MessageSenderApp, MessageReceiverApp { address // executor ) external payable override onlyMessageBus returns (ExecutionStatus) { TransferRequest memory transfer = abi.decode((_message), (TransferRequest)); + wrapBridgeOutToken(_token, _amount); IERC20(_token).safeTransfer(transfer.sender, _amount); bytes memory message = abi.encode(TransferReceipt({nonce: transfer.nonce, status: TransferStatus.Fail})); sendMessage(_sender, _srcChainId, message, msg.value); diff --git a/contracts/message/apps/MsgTest.sol b/contracts/message/apps/MsgTest.sol index ffedcde58..85daede4f 100644 --- a/contracts/message/apps/MsgTest.sol +++ b/contracts/message/apps/MsgTest.sol @@ -61,6 +61,7 @@ contract MsgTest is MessageSenderApp, MessageReceiverApp { address // executor ) external payable override onlyMessageBus returns (ExecutionStatus) { (address receiver, bytes memory message) = abi.decode((_message), (address, bytes)); + wrapBridgeOutToken(_token, _amount); IERC20(_token).safeTransfer(receiver, _amount); emit MessageReceivedWithTransfer(_token, _amount, _sender, _srcChainId, receiver, message); return ExecutionStatus.Success; @@ -73,6 +74,7 @@ contract MsgTest is MessageSenderApp, MessageReceiverApp { address // executor ) external payable override onlyMessageBus returns (ExecutionStatus) { (address receiver, bytes memory message) = abi.decode((_message), (address, bytes)); + wrapBridgeOutToken(_token, _amount); IERC20(_token).safeTransfer(receiver, _amount); emit Refunded(receiver, _token, _amount, message); return ExecutionStatus.Success; diff --git a/contracts/message/apps/TransferSwap.sol b/contracts/message/apps/TransferSwap.sol index 2cc75cfad..f5c0fdf15 100644 --- a/contracts/message/apps/TransferSwap.sol +++ b/contracts/message/apps/TransferSwap.sol @@ -258,6 +258,7 @@ contract TransferSwap is MessageSenderApp, MessageReceiverApp { address // executor ) external payable override onlyMessageBus returns (ExecutionStatus) { SwapRequest memory m = abi.decode((_message), (SwapRequest)); + wrapBridgeOutToken(_token, _amount); require(_token == m.swap.path[0], "bridged token must be the same as the first token in destination swap path"); bytes32 id = _computeSwapRequestId(m.receiver, _srcChainId, uint64(block.chainid), _message); uint256 dstAmount; diff --git a/contracts/message/framework/MessageReceiverApp.sol b/contracts/message/framework/MessageReceiverApp.sol index 751319197..840c6f265 100644 --- a/contracts/message/framework/MessageReceiverApp.sol +++ b/contracts/message/framework/MessageReceiverApp.sol @@ -3,6 +3,9 @@ pragma solidity 0.8.9; import "../interfaces/IMessageReceiverApp.sol"; +import "../interfaces/IMessageBus.sol"; +import "../../interfaces/IBridge.sol"; +import "../../interfaces/IWETH.sol"; import "./MessageBusAddress.sol"; abstract contract MessageReceiverApp is IMessageReceiverApp, MessageBusAddress { @@ -81,4 +84,24 @@ abstract contract MessageReceiverApp is IMessageReceiverApp, MessageBusAddress { bytes calldata _message, address _executor ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {} + + /** + * @notice util function that checks whether the bridge out token is native and wraps it + * @dev when the bridged token is a native or wrapped native token, whether your contracts receives native/wrapped token depends on whether + * the "nativeWrap" field is set in the Bridge contract. An easy but sometimes gas-inefficient way to deal with this is to always check and + * wrap the bridge out token using this function. + * note Assumption: only the liquidity bridge is capable of sending a native wrap + * @param _bridgeOutToken token out from liquidity bridge + * @param _amount amount of token + */ + function wrapBridgeOutToken(address _bridgeOutToken, uint256 _amount) internal { + address bridge = IMessageBus(messageBus).liquidityBridge(); + if (bridge == address(0)) { + return; + } + address nativeWrap = IBridge(bridge).nativeWrap(); + if (_bridgeOutToken == nativeWrap) { + IWETH(nativeWrap).deposit{value: _amount}(); + } + } }