#48983 [SC-Low] Potential Underflow in remove_item() on Empty Array
Submitted on Jul 10th 2025 at 08:05:30 UTC by @Oxenzo_eth for Audit Comp | Folks Smart Contract Library
Report ID: #48983
Report Type: Smart Contract
Report severity: Low
Target: https://github.com/Folks-Finance/algorand-smart-contract-library/blob/main/contracts/library/UInt64SetLib.py
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
When calling remove_item(to_remove, items) on an empty DynamicArray, the code computes last_idx = items.length - 1, which underflows the unsigned integer. Although the function still returns (False, items.copy()), this underflow can introduce unwanted behavior and should be addressed to maintain clarity and correctness.
Vulnerability Details
The remove_item function which is here:
@subroutine
def remove_item(to_remove: UInt64, items: DynamicArray[ARC4UInt64]) -> Tuple[Bool, DynamicArray[ARC4UInt64]]:
last_idx = items.length - 1
for idx, item in uenumerate(items):
if item.native == to_remove:
# remove last item to replace the "to_remove" item or remove entirely if it's the match
last_item = items.pop()
if idx != last_idx:
items[idx] = last_item
# return with the item removed
return Bool(True), items.copy()
# if here then item is not present
return Bool(False), items.copy()begins by calculating -> last_idx = items.length - 1
For an empty array, items.length == 0, so last_idx wraps to the maximum UInt64 value (e.g., 2**64 - 1), due to unsigned underflow. Although the subsequent loop does not fire and the code returns safely, the presence of an invalid last_idx may:
Consume TEAL stack unnecessarily or increase code complexity.
# Potential underflow scenario
items = DynamicArray([])
last_idx = items.length - 1 # underflows -> UInt64 max
for idx, item in uenumerate(items): # loop skipped
...
return Bool(False), items.copy()
Impact Details
Contract fails to deliver promised behavior
References
The function can be located here: https://github.com/Folks-Finance/algorand-smart-contract-library/blob/7673a43fa5183af736b65f17d1a297fdea672059/contracts/library/UInt64SetLib.py#L32
Proof of Concept
Proof of Concept
Kindly place this in the UInt64SetLib.test.ts
describe("remove_item underflow behavior", () => {
test("calling removeItem on empty items array should return false and empty array", async () => {
const { appClient: client } = await factory.deploy();
await localnet.algorand.send.payment({
sender: creator,
receiver: getApplicationAddress(client.appId),
amount: (1).algo(),
});
// Confirm empty array behavior
const result = await client.removeItem({ args: [0n, []] });
expect(result).toEqual([false, []]);
});
});Was this helpful?