Skip to content

Fix: Add pagination to getReceivedTestimonials to prevent DoS from un…#33

Closed
Rav1Chauhan wants to merge 5 commits into
StabilityNexus:mainfrom
Rav1Chauhan:fix/paginated-testimonials
Closed

Fix: Add pagination to getReceivedTestimonials to prevent DoS from un…#33
Rav1Chauhan wants to merge 5 commits into
StabilityNexus:mainfrom
Rav1Chauhan:fix/paginated-testimonials

Conversation

@Rav1Chauhan

@Rav1Chauhan Rav1Chauhan commented Feb 24, 2026

Copy link
Copy Markdown

…bounded array return

Addressed Issues:

Fixes #28
Fix: Prevent DoS via Unbounded Array Return in getReceivedTestimonials
Problem

The previous implementation of:

function getReceivedTestimonials(address receiver)

returned the entire _receivedTestimonials[receiver] dynamic array.

As testimonials accumulate over time, the array grows without bound.
Solidity must copy the entire array from storage to memory when returning it.

This causes:

Gas usage growing linearly with array size

Potential exceeding of block gas limit

Function reverting due to out-of-gas

Inability for users/dApps to retrieve testimonials

Possible Denial of Service (DoS) scenario

Solution

Replaced the unbounded return with pagination:

Added:

getReceivedTestimonialsCount(address receiver)

getReceivedTestimonials(address receiver, uint256 start, uint256 limit)

This ensures:

Bounded gas usage

Safe and scalable retrieval

No change to storage logic

Backward compatibility with existing data

Impact

Prevents potential DoS

Improves scalability

Aligns with Solidity best practices

Screenshots/Recordings:

This change introduces pagination to prevent potential Denial of Service (DoS) caused by returning an unbounded dynamic array.

The previous implementation returned the entire _receivedTestimonials array for a user, which could grow indefinitely over time. As Solidity copies storage arrays to memory when returning them, gas costs grow linearly with array size. Eventually, this could cause the function to revert due to exceeding the block gas limit.

The updated implementation adds:

  • getReceivedTestimonialsCount(address)
  • getReceivedTestimonials(address, uint256 start, uint256 limit)

This ensures scalable and gas-bounded retrieval while preserving existing storage logic..

Additional Notes:

Checklist

  • [ x] My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • [ x] My code follows the project's code style and conventions.
  • If applicable, I have made corresponding changes or additions to the documentation.
  • If applicable, I have made corresponding changes or additions to tests.
  • [ x] My changes generate no new warnings or errors.
  • [ x] I have joined the Stability Nexus's Discord server and I will share a link to this PR with the project maintainers there.
  • [ x] I have read the Contribution Guidelines.
  • [x ] Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.

AI Usage Disclosure

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: TODO

⚠️ AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

Summary by CodeRabbit

Release Notes

  • New Features
    • Enhanced testimonial retrieval with pagination support—fetch testimonials in batches using start position and limit parameters
    • Added a dedicated function to retrieve the count of received testimonials without fetching the full list

@coderabbitai

coderabbitai Bot commented Feb 24, 2026

Copy link
Copy Markdown

Walkthrough

Replaced an unbounded testimonial enumeration function that caused gas-based denial of service with two new accessors: getReceivedTestimonialsCount() and a paginated getReceivedTestimonials(receiver, start, limit). Test invocations updated to use the new pagination API. Extensive multiline formatting applied throughout both files.

Changes

Pagination Accessors & DoS Mitigation

Layer / File(s) Summary
API Signature Changes
contracts/src/VouchMe.sol
Removed getReceivedTestimonials(address) that returned entire array. Added getReceivedTestimonialsCount(address) returning uint256 count and getReceivedTestimonials(address, uint256 start, uint256 limit) returning sliced array with clamped bounds.
Core Pagination Logic
contracts/src/VouchMe.sol
New getReceivedTestimonials computes safe end bound as min(start + limit, total) and returns _receivedTestimonials[receiver][start:end]. Returns empty array when start >= total.
Test Adapter
contracts/test/VouchMe.t.sol
Updated test assertions to query count first via getReceivedTestimonialsCount(), then fetch paginated results via getReceivedTestimonials(alice, 0, count) for both empty and populated cases.
Formatting & Refactoring
contracts/src/VouchMe.sol, contracts/test/VouchMe.t.sol
Multiline reformatting of TestimonialUpdated event parameters, message-hash computation, function signatures, require blocks, and helper function calls; no behavioral changes to core logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

Solidity Lang

Suggested reviewers

  • ceilican

Poem

🐰 Hop, hop—bound no more!
Pagination saves the core,
Gas flows free, not trapped before,
Testimonials fit through the door. 🌟

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding pagination to getReceivedTestimonials to prevent DoS attacks from unbounded array returns.
Linked Issues check ✅ Passed The PR fully implements the pagination solution required by issue #28: replaces unbounded getReceivedTestimonials with bounded paginated accessors (count and start/limit getter).
Out of Scope Changes check ✅ Passed Changes are limited to adding pagination helpers and reformatting for readability; core control flow remains unchanged and aligns with the DoS mitigation objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
contracts/src/VouchMe.sol (3)

156-160: getReceivedTestimonialsCount is a duplicate of the existing getTestimonialCount.

Both functions are external view and return _receivedTestimonials[receiver].length — they are semantically identical. This inflates the ABI surface and creates confusion for integrators about which function to use.

If the intent is to align naming with the new paginated getter, deprecate getTestimonialCount (or remove it if there are no existing off-chain consumers) rather than keeping both.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/src/VouchMe.sol` around lines 156 - 160, The two functions
getReceivedTestimonialsCount and getTestimonialCount both return
_receivedTestimonials[receiver].length and are duplicates; remove one to avoid
ABI/API confusion or mark the older one deprecated. Choose which name to keep
(suggest keeping the paginated-friendly getReceivedTestimonialsCount), delete
the other function declaration (or add a deprecation comment and an event if you
must preserve ABI compatibility), and update any internal references/tests to
call the retained function name.

161-165: Add NatSpec to the new paginated getter and document limit semantics.

Both new functions (getReceivedTestimonialsCount and getReceivedTestimonials) are missing NatSpec @dev/@param/@return comments, unlike the rest of the contract's public API. At minimum, document:

  • that start is zero-based,
  • that the returned slice may be shorter than limit when near the end of the array,
  • that a caller passing limit = 0 receives an empty array.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/src/VouchMe.sol` around lines 161 - 165, Add NatSpec comments to
both getReceivedTestimonialsCount and getReceivedTestimonials: include an `@dev`
describing that these are paginated getters, document `@param` start as zero-based
index, `@param` limit as maximum number of items to return and that passing limit
= 0 yields an empty array, and document `@return` that the returned array is a
slice which may be shorter than limit when near the end of the stored
testimonials; ensure the comments use `@param` for receiver, start, limit and
`@return` for the uint256[] result and match the style of the contract's existing
public API.

375-388: deleteTestimonial does not call _burn, leaving the NFT live on-chain.

After _removeTestimonialFromList runs, the internal accounting (mappings, arrays) is cleaned up, but ownerOf(tokenId) still resolves to the receiver and tokenURI(tokenId) remains accessible. This is a pre-existing gap not introduced by this PR, but worth tracking: any downstream pagination client querying a token ID returned before deletion will still see a valid NFT despite the testimonial being "deleted" from the contract's perspective.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/src/VouchMe.sol` around lines 375 - 388, deleteTestimonial
currently clears internal testimonial mappings via _removeTestimonialFromList
but does not destroy the ERC721 token, so ownerOf(tokenId) and tokenURI(tokenId)
remain valid; after the require checks and after calling
_removeTestimonialFromList in deleteTestimonial, invoke the ERC721 burn routine
(call _burn(tokenId)) to remove the NFT from circulation and ensure
ownerOf/tokenURI no longer resolve, keeping the existing require on
_ownerOf(tokenId) and existing emit TestimonialDeleted(tokenId, msg.sender).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/src/VouchMe.sol`:
- Line 169: Replace the invalid dynamic array allocation "return new uint256;"
with a proper memory array allocation like "return new uint256[](0)" in the
function that currently returns an empty uint256[]; also remove the duplicated
function getReceivedTestimonialsCount() (or getTestimonialCount()) so only one
function returns _receivedTestimonials[receiver].length to avoid redundancy —
locate and update the functions named getReceivedTestimonialsCount and
getTestimonialCount and the return statement referencing new uint256 to apply
these changes.

---

Nitpick comments:
In `@contracts/src/VouchMe.sol`:
- Around line 156-160: The two functions getReceivedTestimonialsCount and
getTestimonialCount both return _receivedTestimonials[receiver].length and are
duplicates; remove one to avoid ABI/API confusion or mark the older one
deprecated. Choose which name to keep (suggest keeping the paginated-friendly
getReceivedTestimonialsCount), delete the other function declaration (or add a
deprecation comment and an event if you must preserve ABI compatibility), and
update any internal references/tests to call the retained function name.
- Around line 161-165: Add NatSpec comments to both getReceivedTestimonialsCount
and getReceivedTestimonials: include an `@dev` describing that these are paginated
getters, document `@param` start as zero-based index, `@param` limit as maximum
number of items to return and that passing limit = 0 yields an empty array, and
document `@return` that the returned array is a slice which may be shorter than
limit when near the end of the stored testimonials; ensure the comments use
`@param` for receiver, start, limit and `@return` for the uint256[] result and match
the style of the contract's existing public API.
- Around line 375-388: deleteTestimonial currently clears internal testimonial
mappings via _removeTestimonialFromList but does not destroy the ERC721 token,
so ownerOf(tokenId) and tokenURI(tokenId) remain valid; after the require checks
and after calling _removeTestimonialFromList in deleteTestimonial, invoke the
ERC721 burn routine (call _burn(tokenId)) to remove the NFT from circulation and
ensure ownerOf/tokenURI no longer resolve, keeping the existing require on
_ownerOf(tokenId) and existing emit TestimonialDeleted(tokenId, msg.sender).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56c2737 and 3534d9f.

📒 Files selected for processing (1)
  • contracts/src/VouchMe.sol

Comment thread contracts/src/VouchMe.sol Outdated
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
contracts/src/VouchMe.sol (1)

156-160: 🛠️ Refactor suggestion | 🟠 Major

Duplicate function: getReceivedTestimonialsCount duplicates getTestimonialCount.

Both functions return _receivedTestimonials[receiver].length. Remove one to avoid confusion and reduce contract bytecode size. Consider keeping getTestimonialCount (lines 213-217) since it already exists and deprecating this new one, or vice versa with a clear naming convention for the pagination API.

♻️ Suggested fix: Remove the duplicate function
-    function getReceivedTestimonialsCount(
-        address receiver
-    ) external view returns (uint256) {
-        return _receivedTestimonials[receiver].length;
-    }

Then use getTestimonialCount(receiver) consistently for pagination, or rename getTestimonialCount to getReceivedTestimonialsCount and remove the old one if you prefer the new naming.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/src/VouchMe.sol` around lines 156 - 160, Remove the duplicate
getter to avoid confusion and reduce bytecode: delete the
getReceivedTestimonialsCount function and use the existing getTestimonialCount
function (which returns _receivedTestimonials[receiver].length) consistently
across the contract and pagination API, or alternatively keep
getReceivedTestimonialsCount and remove/rename getTestimonialCount—ensure only
one of the two remains and update any external references to the retained
function name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/src/VouchMe.sol`:
- Around line 161-185: getReceivedTestimonials was changed to a paginated
signature (address receiver, uint256 start, uint256 limit) which breaks existing
callers and the ABI; update all test calls that still pass a single address to
call getReceivedTestimonials(receiver, 0, <suitable_limit>) (e.g., use total
length or a safe max like 100) and regenerate the contract ABI so
web/src/abis/VouchMe.json matches the new function signature; search for
references to getReceivedTestimonials in tests (e.g., VouchMe.t.sol and
VouchMeFuzz.t.sol) and replace the single-arg calls accordingly, then recompile
and refresh the ABI artifact.

---

Duplicate comments:
In `@contracts/src/VouchMe.sol`:
- Around line 156-160: Remove the duplicate getter to avoid confusion and reduce
bytecode: delete the getReceivedTestimonialsCount function and use the existing
getTestimonialCount function (which returns
_receivedTestimonials[receiver].length) consistently across the contract and
pagination API, or alternatively keep getReceivedTestimonialsCount and
remove/rename getTestimonialCount—ensure only one of the two remains and update
any external references to the retained function name.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 258b753b-cf6e-4a92-883c-c94edbd8d8dc

📥 Commits

Reviewing files that changed from the base of the PR and between 3534d9f and dfc51ab.

📒 Files selected for processing (1)
  • contracts/src/VouchMe.sol

Comment thread contracts/src/VouchMe.sol
Comment on lines +161 to 185
function getReceivedTestimonials(
address receiver,
uint256 start,
uint256 limit
) external view returns (uint256[] memory) {
uint256 total = _receivedTestimonials[receiver].length;

if (start >= total) {
return new uint256[](0);
}

uint256 end = start + limit;
if (end > total) {
end = total;
}

uint256 size = end - start;
uint256[] memory result = new uint256[](size);

for (uint256 i = 0; i < size; i++) {
result[i] = _receivedTestimonials[receiver][start + i];
}

return result;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the VouchMe.sol contract to see all signatures of getReceivedTestimonials
rg -n "function getReceivedTestimonials" contracts/src/VouchMe.sol -A 10

Repository: StabilityNexus/VouchMe

Length of output: 599


🏁 Script executed:

# Check for the old single-argument signature anywhere in the contract
rg -n "getReceivedTestimonials\s*\(" contracts/src/VouchMe.sol -B 2 -A 8

Repository: StabilityNexus/VouchMe

Length of output: 460


🏁 Script executed:

# Check test file at line 290
sed -n '285,295p' contracts/test/VouchMe.t.sol

Repository: StabilityNexus/VouchMe

Length of output: 513


🏁 Script executed:

# Check VouchMeFuzz test file at lines 94 and 177
sed -n '90,98p' contracts/test/VouchMeFuzz.t.sol
sed -n '173,181p' contracts/test/VouchMeFuzz.t.sol

Repository: StabilityNexus/VouchMe

Length of output: 765


🏁 Script executed:

# Check for the getTestimonialCount function mentioned in the suggested fix
rg -n "function getTestimonialCount" contracts/src/VouchMe.sol -A 5

Repository: StabilityNexus/VouchMe

Length of output: 250


🏁 Script executed:

# Check ABI file for the function signature
rg -n "getReceivedTestimonials" web/src/abis/VouchMe.json -B 2 -A 10

Repository: StabilityNexus/VouchMe

Length of output: 28963


Pagination implementation is correct, but this is a breaking change that will cause test compilation failures.

The pagination logic properly bounds gas usage and addresses the DoS concern. However, the new signature requires three parameters (receiver, start, limit), breaking existing callers that pass only the receiver address:

  • contracts/test/VouchMe.t.sol:290 calls getReceivedTestimonials(alice)
  • contracts/test/VouchMeFuzz.t.sol:94 and :177 call getReceivedTestimonials(receiver)
  • web/src/abis/VouchMe.json has stale ABI entries for the old single-argument signature
🔧 Suggested test file updates

For tests, update calls to use the new paginated signature. Example for VouchMe.t.sol:290:

-uint256[] memory remainingTestimonials = vouchMe.getReceivedTestimonials(alice);
+uint256 count = vouchMe.getTestimonialCount(alice);
+uint256[] memory remainingTestimonials = vouchMe.getReceivedTestimonials(alice, 0, count);

Also regenerate the ABI file after contract changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/src/VouchMe.sol` around lines 161 - 185, getReceivedTestimonials
was changed to a paginated signature (address receiver, uint256 start, uint256
limit) which breaks existing callers and the ABI; update all test calls that
still pass a single address to call getReceivedTestimonials(receiver, 0,
<suitable_limit>) (e.g., use total length or a safe max like 100) and regenerate
the contract ABI so web/src/abis/VouchMe.json matches the new function
signature; search for references to getReceivedTestimonials in tests (e.g.,
VouchMe.t.sol and VouchMeFuzz.t.sol) and replace the single-arg calls
accordingly, then recompile and refresh the ABI artifact.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
contracts/test/VouchMe.t.sol (1)

473-480: Consider testing with getTestimonialCount for consistency.

The test passes limit=0 directly. While this works (since start >= total returns empty), it doesn't explicitly verify the "no testimonials exist" scenario the same way other tests do.

For consistency with other tests and clearer intent, consider:

 function testGetReceivedTestimonialsEmpty() public {
+    uint256 count = vouchMe.getTestimonialCount(alice);
+    assertEq(count, 0); // Explicitly verify no testimonials exist
     uint256[] memory testimonials = vouchMe.getReceivedTestimonials(
         alice,
         0,
-        0
+        count
     );
     assertEq(testimonials.length, 0);
 }

This is a minor style suggestion—the current test is functionally correct.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/test/VouchMe.t.sol` around lines 473 - 480, Replace the hardcoded
zero in testGetReceivedTestimonialsEmpty with an explicit count fetched from the
contract: call vouchMe.getTestimonialCount(alice) into a uint256 total and then
call vouchMe.getReceivedTestimonials(alice, total, 0) (or start = total, limit =
0) and assert length == 0; this uses getTestimonialCount to clearly express "no
testimonials exist" like other tests and keeps the assertion behavior the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env:
- Line 1: The repository contains a committed `.env` exposing
NEXT_PUBLIC_PROJECT_ID; remove this credential from the repo and history and
prevent future leaks: add a `.gitignore` entry for `.env`, create a
`.env.example` with a placeholder NEXT_PUBLIC_PROJECT_ID to document required
variables, rotate the exposed WalletConnect Project ID in the WalletConnect
Cloud dashboard, delete the committed `.env` from the repository and purge it
from git history using a history-rewriting tool (e.g., BFG or git
filter-branch), and ensure the new actual `.env` is not tracked going forward.

---

Nitpick comments:
In `@contracts/test/VouchMe.t.sol`:
- Around line 473-480: Replace the hardcoded zero in
testGetReceivedTestimonialsEmpty with an explicit count fetched from the
contract: call vouchMe.getTestimonialCount(alice) into a uint256 total and then
call vouchMe.getReceivedTestimonials(alice, total, 0) (or start = total, limit =
0) and assert length == 0; this uses getTestimonialCount to clearly express "no
testimonials exist" like other tests and keeps the assertion behavior the same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd7ceb6d-2e54-407e-a450-cc8760cbbfef

📥 Commits

Reviewing files that changed from the base of the PR and between dfc51ab and 12d6264.

📒 Files selected for processing (2)
  • .env
  • contracts/test/VouchMe.t.sol

Comment thread .env Outdated
@@ -0,0 +1 @@
NEXT_PUBLIC_PROJECT_ID=38b236298d3253f98d42e08991ad74d3 No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if .env is in .gitignore
echo "Checking .gitignore for .env patterns:"
if [ -f .gitignore ]; then
    grep -n "\.env" .gitignore || echo "No .env pattern found in .gitignore"
else
    echo ".gitignore file not found"
fi

# Check for .env.example
echo -e "\nChecking for .env.example:"
fd -t f ".env.example" --max-depth 2

# Check if there are other .env files committed
echo -e "\nOther .env files in repository:"
fd -t f "^\.env" --hidden

Repository: StabilityNexus/VouchMe

Length of output: 196


Do not commit .env files with credentials to version control.

The .env file with your WalletConnect Project ID is committed to the repository. Even though NEXT_PUBLIC_ credentials are publicly visible at runtime, committing them creates security risks:

  • The Project ID can be abused for quota exhaustion or project impersonation
  • Git history permanently associates this credential with your project
  • No .gitignore currently prevents accidental commits of sensitive files

Required actions:

  1. Create .gitignore and add .env to it
  2. Create .env.example with a placeholder value for documentation
  3. Rotate the exposed Project ID in WalletConnect Cloud
  4. Remove this file from git history using git filter-branch or BFG Repo-Cleaner
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 1-1: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env at line 1, The repository contains a committed `.env` exposing
NEXT_PUBLIC_PROJECT_ID; remove this credential from the repo and history and
prevent future leaks: add a `.gitignore` entry for `.env`, create a
`.env.example` with a placeholder NEXT_PUBLIC_PROJECT_ID to document required
variables, rotate the exposed WalletConnect Project ID in the WalletConnect
Cloud dashboard, delete the committed `.env` from the repository and purge it
from git history using a history-rewriting tool (e.g., BFG or git
filter-branch), and ensure the new actual `.env` is not tracked going forward.

@github-actions

github-actions Bot commented May 5, 2026

Copy link
Copy Markdown

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
contracts/src/VouchMe.sol (2)

195-198: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

NatSpec now conflicts with paginated behavior.

Line 195-198 says “Gets all testimonials received,” but the function now returns a page slice (start, limit). Please update the docstring to avoid integration misuse.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/VouchMe.sol` around lines 195 - 198, Update the NatSpec for the
testimonials retrieval function to reflect its paginated behavior: change the
`@dev` description to state it returns a paginated slice of testimonials for a
receiver and document the pagination parameters `start` and `limit`, and update
the `@return` to say it returns a paginated array/slice of testimonial token IDs
(and if applicable the total count or next cursor) instead of "Gets all
testimonials received". Mention the `receiver`, `start`, and `limit` symbols
explicitly in the docstring so integrators know this function returns only a
subset of testimonials.

95-103: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Use abi.encode instead of abi.encodePacked for signed payloads with dynamic fields.

Line 95-103 packs content, giverName, and profileUrl via abi.encodePacked, which is ambiguous for multiple dynamic types and can validate unintended tuples under the same signature.

Suggested fix
-        bytes32 messageHash = keccak256(
-            abi.encodePacked(
-                senderAddress,
-                msg.sender, // receiver
-                content,
-                giverName,
-                profileUrl
-            )
-        );
+        bytes32 messageHash = keccak256(
+            abi.encode(
+                senderAddress,
+                msg.sender, // receiver
+                content,
+                giverName,
+                profileUrl
+            )
+        );

As per coding guidelines, “Review for common smart contract vulnerabilities.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/VouchMe.sol` around lines 95 - 103, The hash construction for
messageHash uses abi.encodePacked with multiple dynamic fields which is
ambiguous; replace abi.encodePacked with abi.encode when building the keccak256
input in the messageHash computation (the keccak256 call that currently packs
senderAddress, msg.sender, content, giverName, profileUrl) so that dynamic types
are encoded unambiguously and signatures validate correctly.
♻️ Duplicate comments (1)
contracts/src/VouchMe.sol (1)

204-208: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Paginated signature change is still breaking downstream contract consumers.

Line 204-208 changed getReceivedTestimonials to (receiver,start,limit), but referenced generated/web consumers still use the old single-argument API (e.g., web/src/typechain-types/VouchMe.ts:675-679, web/src/app/testimonials/page.tsx:120, web/src/app/dashboard/page.tsx:276). This will fail TS typing/runtime calls until ABI + TypeChain + call sites are synchronized.

As per coding guidelines, “Verify that any modification to contract logic includes corresponding updates to automated tests.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/VouchMe.sol` around lines 204 - 208, The change to
getReceivedTestimonials' signature (now getReceivedTestimonials(address
receiver, uint256 start, uint256 limit)) breaks downstream consumers and
generated TypeChain types; either revert the function signature to the original
single-argument API or update the ABI/TypeChain generation and all call sites to
match the new 3-arg signature. Specifically, update the getReceivedTestimonials
references in generated/web consumers (e.g., VouchMe.ts) and app pages
(web/src/app/testimonials/page.tsx, web/src/app/dashboard/page.tsx) to pass
(receiver, start, limit), regenerate TypeChain/ABI artifacts, and run/update
tests that assert this contract call to ensure ABI, TypeChain, and call sites
are synchronized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@contracts/src/VouchMe.sol`:
- Around line 195-198: Update the NatSpec for the testimonials retrieval
function to reflect its paginated behavior: change the `@dev` description to state
it returns a paginated slice of testimonials for a receiver and document the
pagination parameters `start` and `limit`, and update the `@return` to say it
returns a paginated array/slice of testimonial token IDs (and if applicable the
total count or next cursor) instead of "Gets all testimonials received". Mention
the `receiver`, `start`, and `limit` symbols explicitly in the docstring so
integrators know this function returns only a subset of testimonials.
- Around line 95-103: The hash construction for messageHash uses
abi.encodePacked with multiple dynamic fields which is ambiguous; replace
abi.encodePacked with abi.encode when building the keccak256 input in the
messageHash computation (the keccak256 call that currently packs senderAddress,
msg.sender, content, giverName, profileUrl) so that dynamic types are encoded
unambiguously and signatures validate correctly.

---

Duplicate comments:
In `@contracts/src/VouchMe.sol`:
- Around line 204-208: The change to getReceivedTestimonials' signature (now
getReceivedTestimonials(address receiver, uint256 start, uint256 limit)) breaks
downstream consumers and generated TypeChain types; either revert the function
signature to the original single-argument API or update the ABI/TypeChain
generation and all call sites to match the new 3-arg signature. Specifically,
update the getReceivedTestimonials references in generated/web consumers (e.g.,
VouchMe.ts) and app pages (web/src/app/testimonials/page.tsx,
web/src/app/dashboard/page.tsx) to pass (receiver, start, limit), regenerate
TypeChain/ABI artifacts, and run/update tests that assert this contract call to
ensure ABI, TypeChain, and call sites are synchronized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 58c2c383-81ce-4722-b4b3-8834a6fc5472

📥 Commits

Reviewing files that changed from the base of the PR and between 12d6264 and ac288c9.

📒 Files selected for processing (1)
  • contracts/src/VouchMe.sol

@Rav1Chauhan Rav1Chauhan closed this May 9, 2026
@Rav1Chauhan Rav1Chauhan deleted the fix/paginated-testimonials branch May 9, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unbounded Testimonial Enumeration Enables Gas-Based Denial of Service

1 participant