stake.link

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

SDLPoolSecondary: Approval Exist Even Balance Zero

Summary

Let suppose , I locked come amount and give permission to someone as approval with SDLPool.approva then I go and call SDLPoolSecondary.withdraw all of the amount in it , still approval exists.

Vulnerability Details

Approval should be deleted or removed as you did in SDLPoolPrimary.withdraw, in SDLPoolPrimary , it check it withdraw amount is equal to baseAmount , simple delete the Locks and tokenApprovals[_lockId] on the Lock to create any misleading

SDLPoolPrimary.withdraw

uint256 baseAmount = locks[_lockId].amount;
if (_amount > baseAmount) revert InsufficientBalance();
emit Withdraw(msg.sender, _lockId, _amount);
if (_amount == baseAmount) {
delete locks[_lockId];
delete lockOwners[_lockId];
balances[msg.sender] -= 1;
if (tokenApprovals[_lockId] != address(0)) delete tokenApprovals[_lockId];
emit Transfer(msg.sender, address(0), _lockId);
} else {
locks[_lockId].amount = baseAmount - _amount;
}
effectiveBalances[msg.sender] -= _amount;
totalEffectiveBalance -= _amount;

SDLPoolSecondary.withdraw

if (_amount > baseAmount) revert InsufficientBalance();
// q: what if -mount and basedAmount is some
// we delete that lockted instance in SDL Pool Perminary why not here any specific resaaon
lock.amount = baseAmount - _amount;
effectiveBalances[msg.sender] -= _amount;
totalEffectiveBalance -= _amount;
queuedLockUpdates[_lockId].push(LockUpdate(updateBatchIndex, lock));
queuedRESDLSupplyChange -= int256(_amount);
// q: why we are have approvals and delete approval , how it effect the contract .
// if (tokenApprovals[_lockId] != address(0)) delete tokenApprovals[_lockId];
if (updateNeeded == 0) updateNeeded = 1;
emit QueueWithdraw(msg.sender, _lockId, _amount);

Test

it('should be able to withdraw and burn lock NFT', async () => {
let ts1 = await mintLock()
await time.increase(200 * DAY)
await sdlPool.initiateUnlock(1)
let ts2 = (await ethers.provider.getBlock(await ethers.provider.getBlockNumber())).timestamp
await time.increase(200 * DAY)
let startingBalance = await sdlToken.balanceOf(accounts[0])
await sdlPool.approve(accounts[1], 1)
let beforeWithdrawApprove = await sdlPool.getApproved(1);
console.log("beforeWithdrawApprove",beforeWithdrawApprove)
await sdlPool.withdraw(1, toEther(100))
let afterWithdrawApprove = await sdlPool.getApproved(1);
console.log("beforeWithdrawApprove",afterWithdrawApprove)
// assert.equal(fromEther(await sdlPool.queuedRESDLSupplyChange()), -120)
await updateLocks()
// 100000000000000000000
assert.equal(fromEther(await sdlPool.queuedRESDLSupplyChange()), 0)
assert.equal(fromEther((await sdlToken.balanceOf(accounts[0])).sub(startingBalance)), 100)
// assert.equal(fromEther(await sdlToken.balanceOf(sdlPool.address)), 80)
// assert.equal(fromEther(await sdlPool.totalEffectiveBalance()), 80)
assert.equal(fromEther(await sdlPool.totalStaked()), 0)
}

Impact

When one who got the approval still think he can withdraw some amount , even their was nothing left.

Tools Used

Manual Review

Recommendations

Better is to delete the lock when someone take all of it amount from it and remove the Approval .

Updates

Lead Judging Commences

0kage Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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