[#4] chainPlot()#12
Conversation
Four checks: writer-only access, CID length (46-100 bytes), not sunset, deadline (72h from lastPlotTime if enabled). Updates plotCount and lastPlotTime, emits PlotChained. Target ~47k gas (constant). Fixes #4 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Summary
The function includes the four requested validation gates, but the current plotCount update/emission sequence produces the wrong plotIndex after the genesis plot. That breaks the event stream the indexer relies on.
Findings
- [high]
createStoryline()storesplotCount = 1for the genesis plot and emitsPlotChained(..., 0, ...), butchainPlot()then incrementsplotCountbefore emitting and uses the incremented total asplotIndex. The first chained plot therefore emitsplotIndex = 2instead of1, skipping index1entirely and desynchronizing contract events from the off-chain plots table/indexer assumptions.- File:
src/StoryFactory.sol:158 - Suggestion: emit the next plot index as the pre-increment count (or store a separate total/index convention consistently). For the current data model, with genesis at index
0andplotCount = 1, the firstchainPlot()call should emitplotIndex = 1.
- File:
Decision
Request changes because the current event output would create an incorrect plot index sequence on-chain, which is a behavioral bug rather than a cosmetic issue.
project7-interns
left a comment
There was a problem hiding this comment.
T2b Review — REQUEST CHANGES
Bug: Off-by-one in emitted plotIndex
s.plotCount++;
s.lastPlotTime = block.timestamp;
emit PlotChained(storylineId, s.plotCount, msg.sender, contentCID, contentHash);In createStoryline, the genesis plot emits PlotChained(..., 0, ...) and sets plotCount = 1. When chainPlot runs the first time, plotCount increments from 1 to 2, then the event emits plotIndex = 2. Index 1 is skipped. Every subsequent plot index is off by one.
Fix — capture the index before incrementing:
uint256 plotIndex = s.plotCount;
s.plotCount++;
s.lastPlotTime = block.timestamp;
emit PlotChained(storylineId, plotIndex, msg.sender, contentCID, contentHash);This gives genesis = 0, first chain = 1, second chain = 2, etc.
Minor: No guard for non-existent storylineId
If storylineId doesn't exist (0, or > storylineCount), the storage read returns a zero-initialized struct (writer == address(0)). The require(msg.sender == s.writer) will revert, but with the misleading message "Not writer" instead of "Storyline does not exist". Consider adding an explicit existence check:
require(s.writer != address(0), "Storyline does not exist");Validation checklist
| # | Check | Result |
|---|---|---|
| 1 | Only writer can chain | PASS |
| 2 | CID length 46-100 | PASS |
| 3 | Sunset check | PASS |
| 4 | Deadline check | PASS |
| 5 | State updates (plotCount++, lastPlotTime) | PASS |
| 6 | Event emitted with correct values | FAIL — off-by-one |
| 7 | Non-existent storylineId guard | MINOR — works but misleading error |
The off-by-one is a real bug that will produce incorrect on-chain event logs and break any indexer or frontend relying on sequential plot indices. Please fix before re-requesting review.
Emit plotIndex before incrementing plotCount so genesis=0 and first chained plot=1 (was emitting plotCount after increment, skipping index 1). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
T2b Review: APPROVE
Off-by-one fix confirmed — plotIndex = s.plotCount before s.plotCount++. Genesis=0, first chain=1. All four validations correct.
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
The off-by-one bug in chainPlot() is fixed: the emitted plotIndex now uses the pre-increment count, so genesis remains index 0 and the first chained plot emits index 1. Foundry CI is green on the updated head commit.
Findings
- None.
Decision
Approve because the plot-index sequencing blocker is resolved and the PR now satisfies the merge criteria for issue #4.
Summary
Implements
chainPlot(storylineId, contentCID, contentHash)with all four validations:msg.sender == writer(access control)!sunset(not sunset)block.timestamp <= lastPlotTime + 72 hours(deadline, if enabled)Updates plotCount and lastPlotTime, emits PlotChained.
Fixes #4
Test plan
forge buildsucceeds🤖 Generated with Claude Code