## Summary
`TempleTeleporter` layerzero channel can be blocked if the user bridged their tokens with an insufficient gas to execute bridging on the destination chain.
## Vulnerability Details
- Users can bridge their `$Temple` tokens between chains via `TempleTeleporter.teleport()` function, where the intended tokens to be bridged are burnt from the user on the source chain to be minted for them again on the destination chain, then `_lzSend()` is called to send the bridged message via the authorized layerzero endpoint.
- LayerZero endpoint requires a sufficient amount of gas to be sent with the bridged message to execute the txn on the destination chain, and if an insufficient gas is sent; the txn will revert in the `TempleGold` contract on the destination chain with an out-of-gas error, and this is [catched and stored](https://github.com/LayerZero-Labs/LayerZero/blob/a1fb11a3b9c0ac449291816e71eacede4e36613e/contracts/Endpoint.sol#L120C5-L124C10) by the layerzero endpoint on the destination chain (the payload is stored in `storedPayload[_srcChainId][_srcAddress]`):
```javascript
//@note : LayerZero/contracts/Endpoint.sol
// https://github.com/LayerZero-Labs/LayerZero/blob/a1fb11a3b9c0ac449291816e71eacede4e36613e/contracts/Endpoint.sol#L100C5-L125C6
function receivePayload(uint16 _srcChainId, bytes calldata _srcAddress, address _dstAddress, uint64 _nonce, uint _gasLimit, bytes calldata _payload) external override receiveNonReentrant {
// assert and increment the nonce. no message shuffling
require(_nonce == ++inboundNonce[_srcChainId][_srcAddress], "LayerZero: wrong nonce");
LibraryConfig storage uaConfig = uaConfigLookup[_dstAddress];
// authentication to prevent cross-version message validation
// protects against a malicious library from passing arbitrary data
if (uaConfig.receiveVersion == DEFAULT_VERSION) {
require(defaultReceiveLibraryAddress == msg.sender, "LayerZero: invalid default library");
} else {
require(uaConfig.receiveLibraryAddress == msg.sender, "LayerZero: invalid library");
}
// block if any message blocking
StoredPayload storage sp = storedPayload[_srcChainId][_srcAddress];
require(sp.payloadHash == bytes32(0), "LayerZero: in message blocking");
try ILayerZeroReceiver(_dstAddress).lzReceive{gas: _gasLimit}(_srcChainId, _srcAddress, _nonce, _payload) {
// success, do nothing, end of the message delivery
} catch (bytes memory reason) {
// revert nonce if any uncaught errors/exceptions if the ua chooses the blocking mode
storedPayload[_srcChainId][_srcAddress] = StoredPayload(uint64(_payload.length), _dstAddress, keccak256(_payload));
emit PayloadStored(_srcChainId, _srcAddress, _dstAddress, _nonce, _payload, reason);
}
}
```
- As can be noticed, when another bridging call is made from the same source to the same destination chain again via the same channel (layerzero endpoint); it will not be executed as the channel is blocked since the `storedPayload[_srcChainId][_srcAddress]` is not empty (`require(sp.payloadHash == bytes32(0), "LayerZero: in message blocking")` is not satisfied).
- This occures as the `TempleTeleporter.lzReceive()` doesn't implement a non-blocking mechanism where failed txns are stored to be retried manually later via [`retryPayload()`](https://github.com/LayerZero-Labs/LayerZero/blob/a1fb11a3b9c0ac449291816e71eacede4e36613e/contracts/Endpoint.sol#L127C14-L127C26) on the destination lz endpoint.
## Impact
- Blocking the bridging channel between `TempleTeleporter` contracts resulting in:
1. disabling bridging functionality between the chains that got their channel blocked, until manually retried by the protocol.
2. user who got his bridged call blocked will lose his his `$Temple` tokens until the failed call is retried manually by the protocol team.
## Tools Used
Manual Review.
## Recommendations
In `TempleTeleporter.teleport()` function:
- Add a mechanism to enforce a sufficient gas is sent by the user to bridge his tokens by checking that `msg.value` > the expected gas cost calculated by the `TempleTeleporter.quote()`.
- add a mechanism to store failed received bridging txns in the `TempleTeleporter` contract instead of reverting (non-blocking receive mechanism), so that the bridging channel can't be blocked for whatever reason.