Excess fees are not sent back and stuck in SolConnector.sol
Summary
When the user wants to withdraw funds from vault on Solana, he has to pay the fees for cross-chain service to transfer the withdrawal request to Solana through the Layer Zero cross-chain infrastructure. In case of failed source message, or if after paying fees remained unused amount of native currency or lzToken, user expects the amount to be refunded.
But current refunding mechanism is wrong and causes the fees to be stuck without the possibility of withdrawing them or returning them back to the user.
Root Cause
Link 1
In SolConnector.withdraw()refundAddress is specified as address(this), in this case address(this) is the SolConnector.sol, not the user that initiated the transaction. All excess fees Endpoint will send to SolConnector.sol. Moreover, in SolConnector.sol there is no function that can use the fees or extract them.
Link 2
Also we can see that msg.value not fully used - only _nativeFee will be sent to Endpoint. Remaining amount not sent back and stuck in SolConnector.sol.
uint256 messageValue = _payNative(_fee.nativeFee);
endpoint.send{ value: messageValue }(...);
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
// enable the OApp to pay the native fee
if (msg.value < _nativeFee && address(this).balance < _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
IMPORTANT NOTE
Sending msg.value greater than nativeFee is not a user mistake:
Because cross-chain gas fees are dynamic, this quote should be generated right before calling _lzSend to ensure accurate pricing. Sourсe
Users always need to overpay the fee to send message, thats why any protocols that implementing LayerZero must also implement the refunding mechanism correctly.
Internal pre-conditions
endpoint.quote() returns _fee.nativeFee that is less than msg.value.
External pre-conditions
Endpoint sends excess fees back to _refundAddress.
Attack Path
Amelie makes a withdrawal request and sends msg.value = 0.2 ETH to pay for cross-chain gas fees.
Only 0.15 ETH is used, Amelie expects the remaining 0.05 ETH to be sent to the refundAddress, back to her.
0.05 ETH is stuck in SolConnector.sol.
Impact
User lost all unused amount from msg.value and excess fees.
PoC
No response
Mitigation
Allow user to specify the _refundAddress and send whole msg.value to Endpoint, SolConnector.sol should not hold any excess fees.
Fit Mango Moose
Medium
Excess fees are not sent back and stuck in SolConnector.sol
Summary
When the user wants to withdraw funds from vault on Solana, he has to pay the fees for cross-chain service to transfer the withdrawal request to Solana through the Layer Zero cross-chain infrastructure. In case of failed source message, or if after paying fees remained unused amount of native currency or
lzToken
, user expects the amount to be refunded. But current refunding mechanism is wrong and causes the fees to be stuck without the possibility of withdrawing them or returning them back to the user.Root Cause
Link 1 In
SolConnector.withdraw()
refundAddress
is specified asaddress(this)
, in this caseaddress(this)
is theSolConnector.sol
, not the user that initiated the transaction. All excess fees Endpoint will send toSolConnector.sol
. Moreover, inSolConnector.sol
there is no function that can use the fees or extract them.Link 2 Also we can see that
msg.value
not fully used - only_nativeFee
will be sent to Endpoint. Remaining amount not sent back and stuck inSolConnector.sol
.Sending
msg.value
greater thannativeFee
is not a user mistake:Users always need to overpay the fee to send message, thats why any protocols that implementing LayerZero must also implement the refunding mechanism correctly.
Internal pre-conditions
endpoint.quote()
returns_fee.nativeFee
that is less thanmsg.value
.External pre-conditions
Endpoint sends excess fees back to
_refundAddress
.Attack Path
msg.value
= 0.2 ETH to pay for cross-chain gas fees.refundAddress
, back to her.SolConnector.sol
.Impact
User lost all unused amount from
msg.value
and excess fees.PoC
No response
Mitigation
Allow user to specify the
_refundAddress
and send wholemsg.value
to Endpoint,SolConnector.sol
should not hold any excess fees.