Submitted by Koolex, also found by Audittens and rvierdiiev
In zksync, requestL2Transaction  can be used to send a L1->L2 TX.
Mailbox.requestL2Transaction:L236-L245
An important param is _l2TxGasLimit, which is used to process the TX on L2. The L2 gas limit should include both the overhead for processing the batch and the L2 gas needed to process the transaction itself (i.e. the actual l2GasLimit that will be used for the transaction).
So, _l2TxGasLimit should have an amount thats enough to cover:
To ensure this, there is a validation enforced on requestL2Transaction before pushing the TX to the priorityQueue.
Mailbox._writePriorityOp:L361-L368
This is to make sure the transaction at least get a chance to be executed.
When the bootloader receives the L1->L2 TX, it does the following:
bootloader:L878-L884
gasLimitForTx - is the gas limit for the transactions body.
reservedGas - is the amount of gas that is beyond the operators trust limit to refund it back later.
Note: the operators trust limit is guaranteed to be at least MAX_GAS_PER_TRANSACTION which is at the moment 80000000 (check SystemConfig.json).
bootloader:L901-L906
bootloader:L908-L912
For example, lets say the sender has 10_000_000 gas limit (after deducting the overhead ..etc.), and the TX execution consumed 3_000_000, then the potentialRefund is supposed to be 10_000_000-3_000_000 = 7_000_000. This will be returned to refundRecipient. However, if the actual TX execution fails, then potentialRefund will always be zero. Therefore, no refund for the sender at all. In other words, lets say that the TX execution consumed only 500_000 only till it reverted (for whatever reason). so, the potentialRefund should be 9_500_000 which is not the case since it will be always zero on failure.
Note: obviously this is not applicable if L1->L2 transactions were set to be free. Check the comments here.
This issue occurs due to the fact that near call opcode is used to execute the TX (to avoid 63/64 rule), and when the TX execution fails, near call panic is utilised to avoid reverting the bootloader and to revert minting ether to the user.
bootloader:L980-L988
bootloader:L1665-L1670
Check zkSync specific opcodes: Generally accessible,
Please note that the only way to revert only the near_call frame (and not the parent) is to trigger out of gas error or invalid opcode.
Check Simulations via our compiler: Simulating near_call (in Yul only)
In  ZKSYNC_NEAR_CALL_executeL1Tx,  nearCallPanic() is called in case of TX failure. If we check nearCallPanic() function, we find that it exhausts all the gas of the current frame so that out of gas error is triggered.
bootloader:L1678-L1686
Because of this, no matter how much gas was spent on the TX itself, if it fails, all the unused remaining gas will be burned.
According to the docs zkSync: Batch overhead & limited resources of the batch the refund should be provided at the end of the transaction.
On the surface, this might not look like a critical issue since the lost funds are relatively small. However, this may be true for normal or small transactions unlike computationally intensive tasks which may require big upfront payment. The lost funds will be not negligible.
Please refer to How baseFee works on zkSync
Please note that while there is MAX_TRANSACTION_GAS_LIMIT for the gasLimit, it may go way beyond the MAX_TRANSACTION_GAS_LIMIT (since the contracts can be 10s of kilobytes in size). This is called Trusted gas limit which is provided by the operator.
Please check Trusted gas limit
From this, we conclude that the upper limit for the loss is the cost of the gaslimit provided by the user which could possibly be a big amount of payment to cover complex tasks (assuming the operator provided a trusted gas limit bigger or equal to gaslimit provided by the user).
Its worth mentioning, that this breaks the trust assumptions that the users have, since the users assume that they will always get a refund if it wasnt consumed by their requested TX.
For the reasons explained above, Ive set the severity to high.
We have one test file. This file demonstrates two cases:
Important notes:
To run the test file:
You should get the following output:
One suggestion is to use invalid opcode instead of burning gas.
miladpiri (zkSync) confirmed, but disagreed with severity and commented:
Alex the Entreprenerd (judge) decreased severity to QA and commented:
koolexcrypto (warden) commented:
Alex the Entreprenerd (judge) increased severity to Medium and commented:
