There is a flow in the internal accounting for tracking the total active amount of staked Algo in the state variable TOTAL_ACTIVE_STAKE, this state variable is only incremented by the amount of algo deposited (increased in claim_delayed_mint() and immediate_mint()) when minting xAlgo.
The bug however arises from the fact that in the burn() function we reduce the TOTAL_ACTIVE_STAKE by the amount of the algo recieved from burning the xAlgo.
To simplify the bug:
User A deposits 100 ALGO recieves 100 xALGO, TOTAL_ACTIVE_STAKE = 100 ALGO
After a year the user, accrues some rewards 10 ALGO so the XALGO he owns is now worth 110 ALGO
when the user will try to burn hi 100 xAlgo, he will fail because in burn() we will try to reduce TOTAL_ACTIVE_STAKE (100 ALGO) by 110 ALGO which will underflow. (10 ALGO are locked in the contract)
Vulnerability Details
The vulnerability arises from the fact that Total Active Stake is not tracked correctly leading to an undersestimation which wil result in an underflow in the burn() function.
The TOTAL_ACTIVE_STAKE is only updated in the immediate_mint() and claim_delayed_mint() functions.
The problem however arises because we don't increase the TOTAL_ACTIVE_STAKE when we sync rewards and unclaimed fees for the protocol. This leads to underestimating the TOTAL_ACTIVE_STAKE by the accumalted amounts
This will lead to the actual withdrawable assets of users to be significantly higher than the value stored in the state variable TOTAL_ACTIVE_STAKE. Which will lead to an underflow in the burn() function
Permanent locked funds for the user who withdraws last.
A simplified example to showcase this vulnerability. 2 users A and B.
User A deposits 100 ALGO recieves 100 xALGO, TOTAL_ACTIVE_STAKE = 100 ALGO w8 a year and his 100 xAlgo will be worth 110 ALGO
User B deposits 10 ALGO recieves ~8 xALGO, TOTAL_ACTIVE_STAKE = 110 ALGO (100 from User A and 10 from User B)
User A burns his xAlgo (worth 110 ALGO), TOTAL_ACTIVE_STAKE = 110 - 110 = 0 ALGO
User B can't withdraw his ALGO because when he tries to withdraw reducing 10 from 0 will underflow.
Proof of Concept
Proof of Concept
xpected Result:
> algo-liquid-staking-contracts@0.0.1 test> PYTHONPATH='./contracts' jest --runInBandconsole.log totoal pending stake 0n at Object.log (test/xAlgoConsensusV2.test.ts:1590:15)console.log total active stake 182352954n at Object.log (test/xAlgoConsensusV2.test.ts:1591:15)console.log total unclaimed fees 0n at Object.log (test/xAlgoConsensusV2.test.ts:1592:15)console.log 0n 182352954n 63000000n 0n at Object.log (test/xAlgoConsensusV2.test.ts:1593:15)console.log proposers algo balance 245352954n at Object.log (test/xAlgoConsensusV2.test.ts:1597:15)console.log unclaimable balance 63000000n at Object.log (test/xAlgoConsensusV2.test.ts:1599:15)