## Summary
User will lose his `$Temple` tokens if his bridging is done over two txns (failed -> stored -> retried) where the peer address of the source chain has been changed on the destination chain in between these two txns (failed -> stored -> peer change -> retried but will never be executed due to peer change).
## Vulnerability Details
- `TempleTeleporter` contract enables users from bridging their `$Temple` tokens across chains by **burning** these tokens on the source chain and **minting** them on the destination chain, where their bridging message could be not executed on the destination chain if the bridging gas fees sent is insufficient for the call in the destination chain, resulting in blocking the channel between the source and destination `TempleTeleporter` contracts (the issue is explained in another submitted issue: `TempleTeleporter layerzero channels can be blocked if insufficient gas is sent by the user to bridge their tokens`).
- Since `TempleTeleporter` contract doesn't implement a non-blocking receive mechanism, any failed received bridging call will be stuck in the channel (layerzero endpoint) until it's manually retried in another transaction by calling [`retryPayload()`](https://github.com/LayerZero-Labs/LayerZero/blob/a1fb11a3b9c0ac449291816e71eacede4e36613e/contracts/Endpoint.sol#L127C14-L127C26) on the destination lz endpoint with sufficient gas, and this is done in a separate transaction from the failed one.
**But what could go wrong in between these failed and retried transactions that would result in severe loss for the user?**
In the [parent `lzReceive()`](https://github.com/LayerZero-Labs/LayerZero-v2/blob/7aebbd7c79b2dc818f7bb054aed2405ca076b9d6/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppReceiver.sol#L96C9-L96C64) where `TempleTeleporter._lzReceive()` function is going to be invoked: there's a check made on the peer sender address (`TempleTeleporter` contract address on the source chain) being authorized/set:
```javascript
//@note : LayerZero-v2/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppReceiver.sol
// https://github.com/LayerZero-Labs/LayerZero-v2/blob/7aebbd7c79b2dc818f7bb054aed2405ca076b9d6/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppReceiver.sol#L85C5-L100C6
function lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) public payable virtual {
// Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.
if (address(endpoint) != msg.sender) revert OnlyEndpoint(msg.sender);
// Ensure that the sender matches the expected peer for the source endpoint.
if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);
// Call the internal OApp implementation of lzReceive.
_lzReceive(_origin, _guid, _message, _executor, _extraData);
}
```
so if there's a pending bridging message that hasn't been executed/retried on the destination chain (due to gas limit being underestimated by the sender), then trying to retry the message will fail later if the owner of the `TempleTeleporter` contract of the destination chain changes the peer address of the source chain to be different from the contract address that the user used to bridge his tokens, where`OAppReceiver.lzReceive()` will revert as the following condition will not be satisfied:
```javascript
//@note : LayerZero-v2/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppReceiver.sol
// https://github.com/LayerZero-Labs/LayerZero-v2/blob/7aebbd7c79b2dc818f7bb054aed2405ca076b9d6/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppReceiver.sol#L85C5-L100C6
function lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata _message,
address _executor,
bytes calldata _extraData
) public payable virtual {
//...
// Ensure that the sender matches the expected peer for the source endpoint.
if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);
//...
}
```
where:
```javascript
//@note : LayerZero-v2/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppCore.sol
// https://github.com/LayerZero-Labs/LayerZero-v2/blob/7aebbd7c79b2dc818f7bb054aed2405ca076b9d6/packages/layerzero-v2/evm/oapp/contracts/oapp/OAppCore.sol#L54C3-L59C1
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
bytes32 peer = peers[_eid];
if (peer == bytes32(0)) revert NoPeer(_eid);
return peer;
}
```
## Impact
Users will lose their bridged-but not arrived `$Temple` tokens forever as these tokens are burnt on the source chain and will never be executed (minted) on the destination chain due to peer change.
## Tools Used
Manual Review.
## Recommendations
In `TempleTeleporter` contract: implement a pausable functionality to pause the contract in case the admin changes the peer address of any of the destination chain, so that no delayed/retried-to-be transactions could be executed before changing the peers.