LayerZero requires a bit of native gas token so the relayer can complete the message delivery on the destination chain. The gas which would require for a certain message to be delivered can be estimated by calling the estimateFees()
function on the LayerZero endpoint. Distribution.sol doesn't have any logic that would prompt the user to estimate gas before calling the claim()
function or no logical checks to see if the gas provided is more than the minimum. Providing a less-than-needed amount of gas for the transaction can result in OUT-OF-GAS errors which can get stuck on the endpoint (before reaching the destination chain and being able to be caught by the L2MessageReceiver). While Morpheus does employ a mechanism for storing unsuccessful messages for later retry on the receiver, and uses a non-blocking pattern for failed messages, this wouldn't account for messages that haven't reached destination chain.
When a user calls the Distribution::claim()
function it's expected out of the user to pass the amount of gas needed for the execution of the message delivery, although users are allowed to pass an arbitrary amount of gas which can be the less-than-needed amount for a successful delivery of the message.
L1Sender:
Even though L2MessageReceiver does employ a non-blocking pattern which stores failed messages for a later retry, this wouldn't account for messages which didn't reach the destination chain and got stored in the storedPayload on the endpoint for a later payload retry. This would require for someone/anyone to retryPayload on the endpoint and pay for the remaining gas in order for the message to reach the destination chain.
Even though short term, it can still cause a storedPayload which isn't on the receiver.
LayerZero endpoints do not employ any logic to check whether the transaction has sufficient gas to be executed, as it can be examined from the contracts:
0x3c2269811836af69497E5F486A85D7316753cf62 (Arbitrum)
0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675 (Ethereum)
And in the LayerZero endpoint we can see that in order for the lzReceive to be called it requires a certain amount of gas:
If this fails, it's stored on the source chain which in Morpheus's case would be Ethereum, retrying messages and paying for gas can be costly, especially if it isn't done by the user who initiated the transaction in the first place.
Resources:
https://layerzero.gitbook.io/docs/faq/messaging-properties
https://layerzero.gitbook.io/docs/evm-guides/contract-standards/estimating-message-fees
https://layerzero.gitbook.io/docs/evm-guides/advanced/relayer-adapter-parameters
https://layerzero.gitbook.io/docs/ecosystem/relayer/overview
https://layerzero.gitbook.io/docs/evm-guides/master/how-to-send-a-message
https://layerzero.gitbook.io/docs/ecosystem/relayer/layerzero-relayer
https://layerzero.gitbook.io/docs/troubleshooting/layerzero-integration-checklist
Messages that would run out of gas before reaching the MessageReceiver on the destination chain will be stuck in the storedPayload on the source chain and the funds to be received won't be until the payload has been cleared and the remainder of the gas has been paid for.
Manual Review
Employ logic in the claim()
function on Distribution.sol that would call the estimateFees() function on the endpoint to assess the gas and revert the claim() function if the gas provided is less than the expected one.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.