contracts/src/AgentReviewRegistry.sol:113-117:
for (uint256 i = 0; i < 6; i++) {
goodRatios[i] = uint16((goodCounts[i] * 10000) / count);
neutralRatios[i] = uint16((neutralCounts[i] * 10000) / count);
}
The intermediate goodCounts[i] * 10000 is computed in uint256 (fine), then divided by count to produce a value bounded by 10000. Since 10000 < type(uint16).max (= 65535), the cast itself never truncates — that's OK.
However, the real issue is the unbounded loop in _reviews[tokenId] at line 102:
for (uint256 i = 0; i < count; i++) {
Review memory r = _reviews[tokenId][i];
...
}
This is an external view and has no gas in normal calls, but it copies a full Review struct (storage → memory) per iteration. For agents with thousands of reviews it will exceed block gas if ever called from a state-changing context, and it will eat the eth_call gas cap of many public RPC providers (50M-300M), making the function silently revert for popular agents in the marketplace frontend.
Consider:
- bounding the iteration window (e.g.
getRatingDistribution(tokenId, fromIndex, toIndex)), or
- maintaining incremental
goodCounts/neutralCounts storage as reviews are added.
contracts/src/AgentReviewRegistry.sol:113-117:The intermediate
goodCounts[i] * 10000is computed inuint256(fine), then divided bycountto produce a value bounded by10000. Since10000 < type(uint16).max (= 65535), the cast itself never truncates — that's OK.However, the real issue is the unbounded loop in
_reviews[tokenId]at line 102:This is an
external viewand has no gas in normal calls, but it copies a fullReviewstruct (storage → memory) per iteration. For agents with thousands of reviews it will exceed block gas if ever called from a state-changing context, and it will eat the eth_call gas cap of many public RPC providers (50M-300M), making the function silently revert for popular agents in the marketplace frontend.Consider:
getRatingDistribution(tokenId, fromIndex, toIndex)), orgoodCounts/neutralCountsstorage as reviews are added.