BondNFT.sol: Expired bond can increase accRewardsPerShare multiple times by calling claim function #68
Labels
3 (High Risk)
Assets can be stolen/lost/compromised directly
bug
Something isn't working
duplicate-170
edited-by-warden
satisfactory
satisfies C4 submission criteria; eligible for awards
Lines of code
https://github.com/code-423n4/2022-12-tigris/blob/496e1974ee3838be8759e7b4096dbee1b8795593/contracts/BondNFT.sol#L168-L187
https://github.com/code-423n4/2022-12-tigris/blob/496e1974ee3838be8759e7b4096dbee1b8795593/contracts/Lock.sol#L34-L41
Vulnerability details
Impact
Note: I have submitted a different issue (issue 71) that originates from the same code section. However these are different issues with even somewhat counteracting consequences. By reading both issues closely it should become clear that the issues are different.
The
BondNFT.claim
function (https://github.com/code-423n4/2022-12-tigris/blob/496e1974ee3838be8759e7b4096dbee1b8795593/contracts/BondNFT.sol#L168-L187) can be called by calling theLock.claim
function (https://github.com/code-423n4/2022-12-tigris/blob/496e1974ee3838be8759e7b4096dbee1b8795593/contracts/Lock.sol#L34-L41).In the
BondNFT.claim
function, if rewards are claimed for a bond that has expired in a previous epoch, the rewards that were accrued for the bond for the time after it has been expired, are distributed across all shares by increasing theaccRewardsPerShare
mapping accordingly.This is how
accRewardsPerShare
is updated:The issue is that the
Lock.claim
function and thereby theBondNFT.claim
function can be called multiple times by the bond's owner.This also means that the
_pendingDelta
is distributed across shares multiple times, leading to a higheraccRewardsPerShare
than there are actually rewards.So when enough bonds are released there will eventually not be enough funds to pay back any more bonds.
This results in a loss of funds for some users and an unfair gain of funds for others.
Proof of Concept
I have created a test to walk you through this issue.
First you should implement the following function in the
BondNFT
contract which is used in my test in order to log theaccRewardsPerShare
:Add the following test to the
Withdrawing
test section in09.Bonds.js
:In the test bond 1 and bond 2 are created.
Bond 2 is used to call claim multiple times such that when bond 1 is released, the transaction fails because of insufficient funds.
Tools Used
VSCode
Recommended Mitigation Steps
You should make sure that the
_pendingDelta
is only distributed once.You might create a mapping called
pendingDeltas
that keeps track of the_pendingDelta
that has already been distributed for each bond.When the claim function is then called a second time you can calculate
_pendingDelta = _pendingDelta - pendingDeltas[bondId]
.This is similar to how you keep track of the amount of rewards paid for each bond in the
bondPaid
mapping.The text was updated successfully, but these errors were encountered: