DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

[L-02] Unbounded `matches` Array — `getMatches()` DoS at Scale

[L-02] Unbounded matches Array — getMatches() DoS at Scale

Scope

  • LikeRegistry.sol

Description

Each mutual like pushes to matches[user] without bound. getMatches() returns the entire array. At scale, this exceeds the block gas limit.

Risk

Likelihood: Low — Requires many matches per user.

Impact: Low — View function DoS. Users can still interact but cannot enumerate matches.

Severity: Low

  • SWC: SWC-128 (DoS With Block Gas Limit)

  • CWE: CWE-400 (Uncontrolled Resource Consumption)

  • Evidence Grade: A

Proof of Concept

Alice creates a profile and reciprocally matches with 10 different users. Each mutual like appends to both matches[alice] and matches[user]. After 10 matches, getMatches() returns a 10-element array. In production, a popular user could accumulate thousands of matches, and calling getMatches() would consume gas proportional to the array length, eventually exceeding the block gas limit and making the function uncallable.

function test_FINDING009_unbounded_matches_array() public {
vm.prank(alice); nft.mintProfile("Alice", 25, "ipfs://a");
vm.deal(alice, 100 ether);
for (uint256 i = 0; i < 10; i++) {
address user = address(uint160(1000 + i));
vm.deal(user, 2 ether);
vm.prank(user); nft.mintProfile("User", 25, "ipfs://u");
vm.prank(alice); registry.likeUser{value: 1 ether}(user);
vm.prank(user); registry.likeUser{value: 1 ether}(alice);
}
vm.prank(alice);
assertEq(registry.getMatches().length, 10, "Unbounded growth — no limit");
}

forge test --match-test test_FINDING009_unbounded_matches_array -vvvvPASS

Recommended Mitigation

Replace the unbounded return with a paginated getter. This caps gas consumption per call to a fixed maximum regardless of total match count, preventing the view function from exceeding block gas limits. Callers iterate pages off-chain.

-function getMatches() external view returns (address[] memory) {
- return matches[msg.sender];
-}
+function getMatchCount() external view returns (uint256) {
+ return matches[msg.sender].length;
+}
+
+function getMatches(uint256 offset, uint256 limit) external view returns (address[] memory) {
+ address[] storage all = matches[msg.sender];
+ if (offset >= all.length) return new address[](0);
+ uint256 end = offset + limit > all.length ? all.length : offset + limit;
+ address[] memory page = new address[](end - offset);
+ for (uint256 i = offset; i < end; i++) {
+ page[i - offset] = all[i];
+ }
+ return page;
+}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!