Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/IdentityToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ contract IdentityToken is ERC721, IIdentityToken {
error NonTransferable();

uint256 private _nextTokenId = 1;
uint256 public constant MAX_ENDORSEMENTS = 100;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Expose the cap in IIdentityToken for typed integrators.

Consider adding the getter signature to the interface so clients compiled against IIdentityToken can discover the limit without implementation coupling.

♻️ Suggested interface addition
diff --git a/src/interfaces/IIdentityToken.sol b/src/interfaces/IIdentityToken.sol
@@
 interface IIdentityToken is IERC721, IERC721Metadata {
+    function MAX_ENDORSEMENTS() external view returns (uint256);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/IdentityToken.sol` at line 15, Add a getter declaration for the
MAX_ENDORSEMENTS constant to the IIdentityToken interface so external callers
compiled against the interface can read the cap; specifically add a function
signature matching the public constant getter (function MAX_ENDORSEMENTS()
external view returns (uint256)) to IIdentityToken (referencing MAX_ENDORSEMENTS
and IIdentityToken) and ensure the interface compiles with that new declaration.


// wallet => tokenId (enforce one identity per wallet)
mapping(address => uint256) public ownerToTokenId;
Expand Down Expand Up @@ -160,6 +161,7 @@ contract IdentityToken is ERC721, IIdentityToken {
if (_ownerOf(toTokenId) == address(0)) revert Errors.TargetInvalid();

DataTypes.Endorsement[] storage list = endorsements[toTokenId];
if (list.length >= MAX_ENDORSEMENTS) revert Errors.IndexOutOfBounds();

// prevent duplicate active endorsements
for (uint256 i = 0; i < list.length; i++) {
Expand Down
17 changes: 17 additions & 0 deletions test/IdentityToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,23 @@ contract IdentityTokenTest is Test {
assertEq(revokedAt, 0);
}

function test_RevertIf_MaxEndorsementsReached() public {
vm.prank(alice);
uint256 aliceId = identityToken.mint();

vm.prank(bob);
uint256 bobId = identityToken.mint();

for (uint256 i = 0; i < 100; i++) {
vm.prank(alice);
identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", i)), 0);
}

vm.prank(alice);
vm.expectRevert(Errors.IndexOutOfBounds.selector);
identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", uint256(100))), 0);
Comment on lines +298 to +305
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Avoid hardcoded cap values in the boundary test.

Use identityToken.MAX_ENDORSEMENTS() to keep the test aligned if the cap changes.

♻️ Suggested test update
-        for (uint256 i = 0; i < 100; i++) {
+        uint256 maxEndorsements = identityToken.MAX_ENDORSEMENTS();
+        for (uint256 i = 0; i < maxEndorsements; i++) {
             vm.prank(alice);
             identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", i)), 0);
         }

         vm.prank(alice);
         vm.expectRevert(Errors.IndexOutOfBounds.selector);
-        identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", uint256(100))), 0);
+        identityToken.endorse(
+            aliceId,
+            bobId,
+            keccak256(abi.encodePacked("Connection", maxEndorsements)),
+            0
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (uint256 i = 0; i < 100; i++) {
vm.prank(alice);
identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", i)), 0);
}
vm.prank(alice);
vm.expectRevert(Errors.IndexOutOfBounds.selector);
identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", uint256(100))), 0);
uint256 maxEndorsements = identityToken.MAX_ENDORSEMENTS();
for (uint256 i = 0; i < maxEndorsements; i++) {
vm.prank(alice);
identityToken.endorse(aliceId, bobId, keccak256(abi.encodePacked("Connection", i)), 0);
}
vm.prank(alice);
vm.expectRevert(Errors.IndexOutOfBounds.selector);
identityToken.endorse(
aliceId,
bobId,
keccak256(abi.encodePacked("Connection", maxEndorsements)),
0
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/IdentityToken.t.sol` around lines 298 - 305, The test uses a hardcoded
cap of 100 when filling endorsements; replace that literal with the contract
constant by querying identityToken.MAX_ENDORSEMENTS() into a uint256 (e.g., max)
and loop for (uint256 i = 0; i < max; i++) calling identityToken.endorse(...),
then expect the revert when calling identityToken.endorse with index equal to
max (Errors.IndexOutOfBounds.selector). Update references to the hardcoded 100
to use max and keep aliceId, bobId, identityToken.endorse, and
Errors.IndexOutOfBounds.selector intact.

}

// --- deleteAttribute ---

function test_DeleteAttribute() public {
Expand Down
Loading