Summary
In TempleTeleporter#quote()
function, fee is wrongly calculated due to wrong message sent
Vulnerability Details
In quote()
function, fee is calculated with message is abi.encodePacked(_to, _amount)
:
function quote(
uint32 _dstEid,
address _to,
uint256 _amount,
bytes memory _options
) external view returns (MessagingFee memory fee) {
return _quote(_dstEid, abi.encodePacked(_to, _amount), _options, false);
}
But when sending, message is abi.encodePacked(to.addressToBytes32(), amount)
:
function teleport(
uint32 dstEid,
address to,
uint256 amount,
bytes calldata options
) external payable override returns(MessagingReceipt memory receipt) {
if (amount == 0) { revert CommonEventsAndErrors.ExpectedNonZero(); }
if (to == address(0)) { revert CommonEventsAndErrors.InvalidAddress(); }
bytes memory _payload = abi.encodePacked(to.addressToBytes32(), amount);
temple.burnFrom(msg.sender, amount);
emit TempleTeleported(dstEid, msg.sender, to, amount);
receipt = _lzSend(dstEid, _payload, options, MessagingFee(msg.value, 0), payable(msg.sender));
}
It will lead to message will be difference when claculating and actual fee that necessary. For example:
function encode1(address _address, uint256 amount) public pure returns (bytes memory){
return abi.encodePacked(_address, amount);
}
function encode2(address _address, uint256 amount) public pure returns (bytes memory){
return abi.encodePacked(addressToBytes32(_address), amount);
}
With input is 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5
and 123
:
encode1()
will return 0x95222290dd7278aa3ddd389cc1e1d165cc4bafe5000000000000000000000000000000000000000000000000000000000000007b
encode2()
will return 0x00000000000000000000000095222290dd7278aa3ddd389cc1e1d165cc4bafe5000000000000000000000000000000000000000000000000000000000000007b
And in LayerZero, fee is calculated based on message size (link: https://github.com/LayerZero-Labs/LayerZero-v2/blob/7aebbd7c79b2dc818f7bb054aed2405ca076b9d6/packages/layerzero-v2/evm/messagelib/contracts/SendLibBase.sol#L147
function _quote(
address _sender,
uint32 _dstEid,
uint256 _msgSize,
bool _payInLzToken,
bytes calldata _options
) internal view returns (uint256, uint256) {
(bytes memory executorOptions, WorkerOptions[] memory validationOptions) = _splitOptions(_options);
uint256 nativeFee = _quoteVerifier(_sender, _dstEid, validationOptions);
ExecutorConfig memory config = getExecutorConfig(_sender, _dstEid);
_assertMessageSize(_msgSize, config.maxMessageSize);
nativeFee += ILayerZeroExecutor(config.executor).getFee(_dstEid, _sender, _msgSize, executorOptions);
(uint256 treasuryNativeFee, uint256 lzTokenFee) = _quoteTreasury(_sender, _dstEid, nativeFee, _payInLzToken);
nativeFee += treasuryNativeFee;
return (nativeFee, lzTokenFee);
}
Which will lead to wrong fee returned. As actual fee bigger than calculated fee, it will lead to msg revert if user rely on it.
Impact
User are not able to send message if thet are rely on fee returned in TempleTeleporter#quote()
function
Tools Used
Manual review.
Recommendations
Update quote()
function to:
function quote(
uint32 _dstEid,
address _to,
uint256 _amount,
bytes memory _options
) external view returns (MessagingFee memory fee) {
- return _quote(_dstEid, abi.encodePacked(_to, _amount), _options, false);
+ return _quote(_dstEid, abi.encodePacked(_to.addressToBytes32(), _amount), _options, false);
}