stake.link

stake.link
DeFiHardhatBridge
27,500 USDC
View results
Submission Details
Severity: medium
Invalid

The `WrappedTokenBridge::_transferTokens` function does not offer protection against an undesired amount of wrapped token output

Summary

The WrappedTokenBridge::_transferTokens function does not offer protection against an undesired amount of wrapped token output

Vulnerability Details

The WrappedTokenBridge::_transferTokens function helps to send wrapped tokens to a destination chain, the token is transferred to the contract, then the deposited amount is converted to a number of wrapped tokens and finally it is sent to the destination chain:

File: WrappedTokenBridge.sol
158: function _transferTokens(
159: uint64 _destinationChainSelector,
160: address _sender,
161: address _receiver,
162: uint256 _amount,
163: bool _payNative,
164: uint256 _maxLINKFee
165: ) internal returns (bytes32 messageId) {
166: uint256 preWrapBalance = wrappedToken.balanceOf(address(this));
167: wrappedToken.wrap(_amount);
168: uint256 amountToTransfer = wrappedToken.balanceOf(address(this)) - preWrapBalance;
169:
170: Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(
171: _receiver,
172: amountToTransfer,
173: _payNative ? address(0) : address(linkToken)
174: );
175:
176: IRouterClient router = IRouterClient(this.getRouter());
177: uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
178:
179: if (_payNative) {
180: if (fees > msg.value) revert InsufficientFee();
181: messageId = router.ccipSend{value: fees}(_destinationChainSelector, evm2AnyMessage);
182: if (fees < msg.value) {
183: (bool success, ) = _sender.call{value: msg.value - fees}("");
184: if (!success) revert TransferFailed();
185: }
186: } else {
187: if (fees > _maxLINKFee) revert FeeExceedsLimit();
188: linkToken.safeTransferFrom(_sender, address(this), fees);
189: messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
190: }
191:
192: emit TokensTransferred(
193: messageId,
194: _destinationChainSelector,
195: _sender,
196: _receiver,
197: amountToTransfer,
198: _payNative ? address(0) : address(linkToken),
199: fees
200: );
201: return messageId;
202: }

The problem is that the function WrappedTokenBridge::_transferTokens does not offer a protection to NOT execute the transaction if the amount of wrapped tokens is not a convenient amount for the user. This can happen if the transaction is in process in the mempool and while it is there, the number of wrapped tokens changes to an unwanted amount, causing the user to lose tokens.

Impact

The user may receive an undesired amount of wrapped tokens because the transaction may be executed in an unforeseen amount of time, causing the amount of wrapped tokens that the user receives to be an undesired amount.

Tools used

Manual review

Recommendations

Add a protection so that the transaction is not executed if the number of wrapped tokens is not desired:

function _transferTokens(
uint64 _destinationChainSelector,
address _sender,
address _receiver,
uint256 _amount,
bool _payNative,
uint256 _maxLINKFee,
++ uint256 _minWrappedTokens,
) internal returns (bytes32 messageId) {
uint256 preWrapBalance = wrappedToken.balanceOf(address(this));
wrappedToken.wrap(_amount);
uint256 amountToTransfer = wrappedToken.balanceOf(address(this)) - preWrapBalance;
++ if (amountToTransfer < _minWrappedTokens) revert();
...
...
...
Updates

Lead Judging Commences

0kage Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.