DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

Unbounded Match Array Growth Leading to Gas Inefficiency

Summary

The LikeRegistry contract uses a mapping (matches) that stores arrays of addresses for each user’s matches. Repeated matches cause these arrays to grow indefinitely, increasing gas costs for the getMatches function and risking transaction failures due to block gas limits. Over time, this makes the contract increasingly inefficient and potentially unusable.


Vulnerability Details

Location

  • Contract: LikeRegistry.sol

  • Mapping: matches[address] (stores address[])

Root Cause

  1. Unbounded Array Growth:

    • Every time a mutual "like" occurs, both users’ addresses are appended to each other’s matches array.

    • There is no mechanism to limit the size of these arrays (e.g., removing old matches).

  2. Gas Costs:

    • Reading large arrays via getMatches becomes prohibitively expensive as the array grows.

    • Transactions that interact with the array (e.g., iterating over matches) may exceed block gas limits.

Code Snippet

// In LikeRegistry.sol
mapping(address => address[]) public matches;
function likeUser(address liked) external payable {
// ...
if (likes[liked][msg.sender]) {
matches[msg.sender].push(liked); // Array grows indefinitely
matches[liked].push(msg.sender);
// ...
}
}

Impact

Severity: Medium

  • Gas Inefficiency: The getMatches function’s gas cost scales linearly with the number of matches, making it expensive for long-term users.

  • Denial of Service (DoS): Applications or users calling getMatches may encounter out-of-gas errors for large arrays.

  • Storage Bloat: The contract’s storage usage grows indefinitely, increasing blockchain bloat.


Expected vs Actual Behavior

  • Expected: The matches array should have a manageable size, and getMatches should remain gas-efficient.

  • Actual: The matches array grows linearly, and getMatches gas costs increase proportionally.


Recommended Mitigation

Use a Mapping + Counter

Replace the array with a mapping to track matches more efficiently:

struct MatchHistory {
mapping(uint256 => address) matches;
uint256 count;
}
mapping(address => MatchHistory) private _matches;
function likeUser(address liked) external payable {
// ...
if (likes[liked][msg.sender]) {
MatchHistory storage senderMatches = _matches[msg.sender];
senderMatches.matches[senderMatches.count++] = liked;
// ...
}
}
// Fetch matches with pagination
function getMatches(address user, uint256 offset, uint256 limit) external view returns (address[] memory) {
MatchHistory storage history = _matches[user];
uint256 end = offset + limit;
if (end > history.count) end = history.count;
address[] memory result = new address[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = history.matches[i];
}
return result;
}

Additional Recommendations

  1. Match Expiry: Automatically remove old matches after a certain period.

  2. Event-Based Tracking: Emit events for matches and let off-chain systems track them (reduces on-chain storage).

P.S. We know that realistically, no one is racking up this many matches—this isn’t Tinder Premium on steroids. But hey, just in case someone actually gets that lucky (or the blockchain gets that desperate), I thought a security report was in order!

Updates

Appeal created

n0kto Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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