stake.link

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

SDLPoolSecondary::Owner of a lock is overridden

Summary

When a user stake and lock its position in one batch a lockID is assigned for that lock, the owner of the lockID is overridden in next batch.

Vulnerability Details

In SDLPoolSecondary contract staked position [locked/normal] are sent to primary chain in a batch, handleOutgoingUpdate() and handleIncomingUpdate() is used to set a batch. When in a batch a user first stake a position and lock it it's lockID is set to 1. In another batch when a user first stake a position and lock it it's lockID also sets to 1. After executing each of batches if the first user of 1st batch wants to initiate unlock for his lockID i.e 1 it will revert because the owner of lockID 1 is overridden by the first user of 2nd batch. Now this address will be replaced by the first staker of 3rd batch and this process will go on.

POC

Run this test:

it('duplicate Id', async () => {
// FIRST BATCH
await sdlToken.transferAndCall( // staking [locked position]
sdlPool.address,
toEther(100),
ethers.utils.defaultAbiCoder.encode(['uint256', 'uint64'], [0, 150 * DAY]) //queued
)
// processing the queued stake
await sdlPool.handleOutgoingUpdate()
await sdlPool.handleIncomingUpdate(1)
// executing the queued stake
await sdlPool.executeQueuedOperations([])
await time.increase(120 * DAY)
console.log(await sdlPool.getLockIdsByOwner(accounts[0]))
console.log(await sdlPool.ownerOf(1))
//SECOND BATCH
await sdlToken // staking[locked]
.connect(signers[1])
.transferAndCall(
sdlPool.address,
toEther(1000),
ethers.utils.defaultAbiCoder.encode(['uint256', 'uint64'], [0, 365 * DAY])
)
await time.increase(120 * DAY)
//processing the queued stake
await sdlPool.handleOutgoingUpdate()
await sdlPool.handleIncomingUpdate(1)
// executing the queued stake
await sdlPool.connect(signers[1]).executeQueuedOperations([])
await time.increase(120 * DAY)
console.log(await sdlPool.getLockIdsByOwner(accounts[1]))
console.log(await sdlPool.ownerOf(1))
await expect(sdlPool.initiateUnlock(1)).to.be.reverted
await expect(sdlPool.connect(signers[1]).initiateUnlock(1)).not.to.be.reverted
})

Output:

SDLPoolSecondary
[ BigNumber { value: "1" } ]
0x11187eff852069a33d102476b2E8A9cc9167dAde
[ BigNumber { value: "1" } ]
0x2228bdc8584595DfefA75597C96B13c00a2D88C2
✔ duplicate Id (227ms)
1 passing (3s)

Here you can see how the owner of a lock is overridden & how the first owner failed to initiate unlock his lock.

Impact

A user can lost ownership of his lock.

Tools Used

Manual analysis.

Recommendations

lockID should be incremented by 1 for every lock.

Updates

Lead Judging Commences

0kage Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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