Summary
The tokenApprovals[_lockId] is not deleted if the ERC721 is transferred between multiple chains.
Vulnerability Details
The lockId can be transferred from the primary chain to the secondary chain with the help of the function RESDLTokenBridge::transferRESDL, this will transfer the token to the destination chain. Likewise, you can specify the receiver, that is, in transfer between chains the recipient can be another user.
The problem is that when transferring between chains, the tokenApprovals[_lockId] variable is not cleared. Keep in mind that when the lockId is transferred within the same chain tokenApprovals[_lockId] is deleted:
File: SDLPool.sol
455: function _transfer(
456: address _from,
457: address _to,
458: uint256 _lockId
459: ) internal virtual {
460: if (_from != ownerOf(_lockId)) revert TransferFromIncorrectOwner();
461: if (_to == address(0)) revert TransferToZeroAddress();
462: if (_to == ccipController) revert TransferToCCIPController();
463:
464: delete tokenApprovals[_lockId];
465:
466: _updateRewards(_from);
467: _updateRewards(_to);
468:
469: uint256 effectiveBalanceChange = locks[_lockId].amount + locks[_lockId].boostAmount;
470: effectiveBalances[_from] -= effectiveBalanceChange;
471: effectiveBalances[_to] += effectiveBalanceChange;
472:
473: balances[_from] -= 1;
474: balances[_to] += 1;
475: lockOwners[_lockId] = _to;
476:
477: emit Transfer(_from, _to, _lockId);
478: }
Impact
The tokenApprovals[_lockId] will be cleared when the ERC721 is transferred within the same chain however tokenApprovals[_lockId] is NOT cleared when the transfer is made between multiple chains.
Tools used
Manual review
Recommendations
Clear tokenApprovals[_lockId] when transferring ERC721 on multiple chains:
function handleOutgoingRESDL(
address _sender,
uint256 _lockId,
address _sdlReceiver
)
external
onlyCCIPController
onlyLockOwner(_lockId, _sender)
updateRewards(_sender)
updateRewards(ccipController)
returns (Lock memory)
{
Lock memory lock = locks[_lockId];
++ // remove token approvals
delete locks[_lockId].amount;
delete lockOwners[_lockId];
balances[_sender] -= 1;
uint256 totalAmount = lock.amount + lock.boostAmount;
effectiveBalances[_sender] -= totalAmount;
effectiveBalances[ccipController] += totalAmount;
sdlToken.safeTransfer(_sdlReceiver, lock.amount);
emit OutgoingRESDL(_sender, _lockId);
return lock;
}