# #49003 \[SC-Low] Array Underflow Vulnerability in UInt64SetLib leads to contract failure

**Submitted on Jul 10th 2025 at 12:44:28 UTC by @HandsomeEarthworm6 for** [**Audit Comp | Folks Smart Contract Library**](https://immunefi.com/audit-competition/folks-sc-library)

* **Report ID:** #49003
* **Report Type:** Smart Contract
* **Report severity:** Low
* **Target:** <https://github.com/Folks-Finance/algorand-smart-contract-library/blob/main/contracts/library/UInt64SetLib.py>
* **Impacts:**
  * Temporary denial of service (smart contract is made unable to operate for one block, functionality is restored in the next block)

## Description

## Brief/Intro

The UInt64SetLib.py contract contains an integer underflow vulnerability in the remove\_item function that causes temporary contract failure when attempting to remove items from empty arrays.

## Vulnerability Details

The vulnerability exists in the remove\_item\_for\_header function of contracts/library/UInt64SetLib.py:

last\_idx = items.length - 1 # Critical underflow when items.length = 0

When items.length equals 0, the subtraction operation 0 - 1 causes an integer underflow in the underlying TEAL assembly code. Since Algorand uses unsigned 64-bit integers (UInt64), this underflow wraps the value to UInt64::MAX. The contract then attempts to access items\[18446744073709551615], which immediately fails with an array bounds error as shown below. intc\_0 // 0 <- Push 0 (array length) extract\_uint16 <- Extract as 16-bit uint dup <- Duplicate the length value intc\_1 // 1 <- Push 1

* <--- Error <- Subtraction causes underflow and crash

The vulnerability affects multiple code paths:

Direct calls to removeItem() with empty arrays

Cascading failures when arrays become empty through normal operations

Batch operations that process items sequentially

Test Results Confirmation: Six separate test cases failed with identical underflow errors, confirming the vulnerability affects:

1.remove\_item on empty array should not crash

2.remove\_item on empty array with different values

3.single item removal leading to empty array

4.operations on empty arrays

5.consecutive operations on empty arrays

## Impact Details

Contract Failure: Any call to remove\_item with an empty array immediately crashes the contract

\##Recommendation check if the array is empty before trying to calculate last\_idx

## Proof of Concept

## Proof of Concept

add this test to tests/library/UInt64SetLib.test.ts and then run :;

npx jest tests/library/UInt64SetLib.test.ts --testTimeout=30000

/ Add these test cases to your existing describe("UInt64SetLib") block

describe("Security Vulnerability Tests", () => { describe("Critical Issue: Array Underflow on Empty Array", () => { test("remove\_item on empty array should not crash", async () => { // This test will likely FAIL and expose the critical bug const result = await client.removeItem({ args: \[1n, \[]] }); expect(result).toEqual(\[false, \[]]); });

```
  test("remove_item on empty array with different values", async () => {
    expect(await client.removeItem({ args: [0n, []] })).toEqual([false, []]);
    expect(await client.removeItem({ args: [999999n, []] })).toEqual([false, []]);
  });
});

describe("Edge Cases", () => {
  test("single item removal leading to empty array", async () => {
    // Remove the only item to create empty array
    const removeResult = await client.removeItem({ args: [42n, [42n]] });
    expect(removeResult).toEqual([true, []]);
    
    // Try to remove from the now-empty result
    const removeFromEmpty = await client.removeItem({ args: [42n, removeResult[1]] });
    expect(removeFromEmpty).toEqual([false, []]);
  });

  test("operations on empty arrays", async () => {
    // Test has_item on empty array
    expect(await client.hasItem({ args: [1n, []] })).toBeFalsy();
    expect(await client.hasItem({ args: [0n, []] })).toBeFalsy();
    
    // Test add_item on empty array
    expect(await client.addItem({ args: [1n, []] })).toEqual([true, [1n]]);
    expect(await client.addItem({ args: [0n, []] })).toEqual([true, [0n]]);
    
    // Test remove_item on empty array
    expect(await client.removeItem({ args: [1n, []] })).toEqual([false, []]);
    expect(await client.removeItem({ args: [0n, []] })).toEqual([false, []]);
  });

  test("consecutive operations on empty arrays", async () => {
    // Multiple remove operations on empty arrays
    for (let i = 0; i < 3; i++) {
      const result = await client.removeItem({ args: [BigInt(i), []] });
      expect(result).toEqual([false, []]);
    }
  });
});

describe("Resource Tests", () => {
  test("operations on large arrays", async () => {
    // Create a moderately large array
    const largeArray = Array.from({ length: 50 }, (_, i) => BigInt(i));
    
    // These should complete without issues
    const hasResult = await client.hasItem({ args: [25n, largeArray] });
    expect(typeof hasResult).toBe("boolean");
    
    const addResult = await client.addItem({ args: [999n, largeArray] });
    expect(Array.isArray(addResult)).toBe(true);
    
    const removeResult = await client.removeItem({ args: [25n, largeArray] });
    expect(Array.isArray(removeResult)).toBe(true);
  });
});
```

});


---

# 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-smart-contract-library/49003-sc-low-array-underflow-vulnerability-in-uint64setlib-leads-to-contract-failure.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.
