# #37903 \[SC-High] "Potential Underflow Vulnerability in burn Function for total\_active\_stake\_key"

**Submitted on Dec 18th 2024 at 15:04:20 UTC by @danvinci\_20 for** [**Audit Comp | Folks: Liquid Staking**](https://immunefi.com/audit-competition/folks-finance-liquid-staking-audit-competition)

* **Report ID:** #37903
* **Report Type:** Smart Contract
* **Report severity:** High
* **Target:** <https://github.com/Folks-Finance/algo-liquid-staking-contracts/blob/8bd890fde7981335e9b042a99db432e327681e1a/contracts/xalgo/consensus\\_v2.py>
* **Impacts:**
  * Temporary freezing of funds for at least 1 hour
  * Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)

## Description

## Brief/Intro

In the current implementation of the `consensus_v2.py` the burn functionality does not explicitly check if the algo\_send is less than the total\_active\_stake before updating the total\_active\_stake\_key.

## Vulnerability Details

This is the code that contains this issue:

```
@router.method(no_op=CallConfig.CALL)
def burn(send_xalgo: abi.AssetTransferTransaction, min_received: abi.Uint64) -> Expr:
    burn_amount = send_xalgo.get().asset_amount()
    algo_balance = ScratchVar(TealType.uint64)
    algo_to_send = ScratchVar(TealType.uint64)

    return Seq(
        rekey_and_close_to_check(),
        # ensure initialised
        Assert(App.globalGet(initialised_key)),
        # check xALGO sent
        check_x_algo_sent(send_xalgo),
        # update total rewards
        update_total_rewards_and_unclaimed_fees(),
        # calculate algo amount to send
        algo_balance.store(
            App.globalGet(total_active_stake_key)
            + App.globalGet(total_rewards_key)
            - App.globalGet(total_unclaimed_fees_key)
        ),
        algo_to_send.store(
            mul_scale(
                burn_amount,
                algo_balance.load(),
                get_x_algo_circulating_supply() + burn_amount
            )
        ),
        # check amount and send ALGO to user
        Assert(algo_to_send.load()),
        Assert(algo_to_send.load() >= min_received.get()),
        send_algo_from_proposers(Txn.sender(), algo_to_send.load()),
        # update total active stake
        App.globalPut(total_active_stake_key, App.globalGet(total_active_stake_key) - algo_to_send.load()),
        # log burn
        Log(Concat(
            MethodSignature("Burn(address,uint64,uint64)"),
            Txn.sender(),
            Itob(burn_amount),
            Itob(algo_to_send.load()),
        )),
    )
```

When a legitimate user tries to burn an amount of x\_algo which equivalent value in algo is greater than the current total\_active\_key will not be possible this leading to temporary freezing of funds for that user. The cause of this is that during the execution of the burn call we updated the the total\_active\_stake value like this

```
 update total active stake
        App.globalPut(total_active_stake_key, App.globalGet(total_active_stake_key) - algo_to_send.load()),
```

This should have been implemented like this

```
If(
            algo_to_send.load() > App.globalGet(total_active_stake_key),
            # set total_active_stake_key to zero if burn amount exceeds it
            App.globalPut(total_active_stake_key, Int(0)),
            # proceed with the normal burn logic
            App.globalPut(total_active_stake_key, App.globalGet(total_active_stake_key) - algo_to_send.load())
        )
```

to prevent an underflow since if algo\_to\_send > total\_active\_stake there will be a revert which should ordinarily shouldn't have occurred and this leads to denial of service for that particular user.

Note: `Algo_balance = total_active_staked + total_rewards - unclaimed_fees`

## Impact Details

The likelihood of this occuring is very low but it will impacts the protocol users preventing from claiming yield temporarily and this could have been prevented

## Proof of Concept

## Proof of Concept

Test 1 invalid

```
class LiquidStakingProtocol:
    def __init__(self):
        self.total_active_stake = 1000  # Initial active stake (example value)
        self.total_rewards = 300  # Example value for rewards
        self.total_unclaimed_fees = 50  # Example value for unclaimed fees
        self.xalgo_circulating_supply = 0 # Example value for circulating supply

    def burn(self, burn_amount, min_received):
        
        # Calculate the algo balance to send
        algo_balance = self.total_active_stake + self.total_rewards - self.total_unclaimed_fees

        # Calculate the amount of ALGO to send based on the burn amount
        algo_to_send = (burn_amount * algo_balance) / (self.xalgo_circulating_supply + burn_amount)
print(f"Algo to send {algo_to_send}")

        # Check if the algo_to_send exceeds the total_active_stake
        if algo_to_send > self.total_active_stake:
            print(f"Error: burn amount too large, setting total active stake to underflow.")
            self.total_active_stake = 0  # Set total active stake to zero
            return
            
        else:
            # Proceed with the normal burn logic
            print(f"Burning {algo_to_send} ALGO from the active stake.")
            self.total_active_stake -= algo_to_send  # Update total active stake

        # Check if the amount sent is greater than or equal to min_received
        if algo_to_send >= min_received:
            print(f"Burn successful. {algo_to_send} ALGO sent.")
        else:
            print(f"Burn failed. Minimum required {min_received} ALGO not received.")

protocol = LiquidStakingProtocol()

# Test 1: Attempt to burn more than the total_active_stake
print("Test 1: Attempting to burn more than the total active stake:")
protocol.burn(1000, 100)  # Attempt to burn more than available

```

Test 2 valid

```
class LiquidStakingProtocol:
    def __init__(self):
        self.total_active_stake = 1000  # Initial active stake (example value)
        self.total_rewards = 300  # Example value for rewards
        self.total_unclaimed_fees = 50  # Example value for unclaimed fees
        self.xalgo_circulating_supply = 1000 # Example value for circulating supply

    def burn(self, burn_amount, min_received):
        
        # Calculate the algo balance to send
        algo_balance = self.total_active_stake + self.total_rewards - self.total_unclaimed_fees

        # Calculate the amount of ALGO to send based on the burn amount
        algo_to_send = (burn_amount * algo_balance) / (self.xalgo_circulating_supply + burn_amount)
        print(f"Algo to send {algo_to_send}")

        # Check if the algo_to_send exceeds the total_active_stake
        if algo_to_send > self.total_active_stake:
            print(f"Error: burn amount too large, setting total active stake to underflow.")
            self.total_active_stake = 0  # Set total active stake to zero
            return
            
        else:
            # Proceed with the normal burn logic
            print(f"Burning {algo_to_send} ALGO from the active stake.")
            self.total_active_stake -= algo_to_send  # Update total active stake

        # Check if the amount sent is greater than or equal to min_received
        if algo_to_send >= min_received:
            print(f"Burn successful. {algo_to_send} ALGO sent.")
        else:
            print(f"Burn failed. Minimum required {min_received} ALGO not received.")


protocol = LiquidStakingProtocol()


#Test 2: Attempt to burn less than the total_active_stake
print("Test 2: Attempting to burn less than the total active stake:")
protocol.burn(500, 100)  # A valid burn

print(f"Total active stake after burn attempt: {protocol.total_active_stake}\n")
```

Summary: `Test 1`: The algo\_to\_send exceeds the available total\_active\_stake, so the stake underflows. `Test 2`: The algo\_to\_send is valid, and the total\_active\_stake is reduced accordingly.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://reports.immunefi.com/folks-liquid-staking/37903-sc-high-potential-underflow-vulnerability-in-burn-function-for-total_active_stake_key.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
