Skip to content

feat: implement StreamCreated handler#43

Open
d3vobed wants to merge 2 commits into
Fundable-Protocol:devfrom
d3vobed:feat/stream-created-handler
Open

feat: implement StreamCreated handler#43
d3vobed wants to merge 2 commits into
Fundable-Protocol:devfrom
d3vobed:feat/stream-created-handler

Conversation

@d3vobed

@d3vobed d3vobed commented Jun 26, 2026

Copy link
Copy Markdown

Summary

Implements the StreamCreated event handler for the payment streams domain as specified in #34.

Changes

  • Parse: parseStreamCreatedPayload validates the event topic matches StreamCreated and extracts the structured payload (streamId, sender, recipient, amount, startTime, endTime)
  • Map: mapStreamCreatedToRecord combines the parsed payload with event identity metadata into a StreamRecord
  • Identity: getEventIdentity produces a deterministic string from contractId:ledger:txHash:eventIndex for idempotency checks
  • Handler: handleStreamCreated composes the above into a single call, returning both the stream record and identity

Testing

  • Payload parsing with valid and invalid topics
  • Identity derivation and uniqueness across all identity fields
  • Record mapping correctness
  • Handler idempotency (same input → same output)
  • Multiple stream ID handling

Verification

  • bun run indexer:type-check
  • bun run indexer:test ✅ (10 tests)
  • bun run indexer:lint

Closes #34

Summary by CodeRabbit

  • New Features
    • Added processing for stream-created events, producing stream records and a deterministic event identity.
    • Exposed stream-created handler utilities, topic constant, and related event/record types for integration.
  • Bug Fixes
    • Strengthened payload validation with clear error handling for malformed topics/data and missing/invalid fields.
    • Ensured handler output is idempotent for identical events and varies when the embedded stream ID changes.
  • Tests
    • Added Vitest coverage for parsing, identity generation, record mapping, and end-to-end handler behavior.

- Add StreamCreated event handler for parsing Soroban events
- Map event payload to Stream record with event identity tracking
- Add deterministic event identity for idempotency checks
- Add mocked payload tests covering:
  - Payload parsing and validation
  - Event identity derivation
  - Record mapping
  - Handler idempotency
  - Multiple stream ID handling
@drips-wave

drips-wave Bot commented Jun 26, 2026

Copy link
Copy Markdown

@d3vobed Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds StreamCreated event types, payload validation, identity generation, record mapping, handler composition, package exports, and Vitest coverage for parsing and handler behavior.

Changes

StreamCreated handler

Layer / File(s) Summary
Event contract and parsing
indexer/streams/src/handlers/types.ts, indexer/streams/src/handlers/streamCreated.ts, indexer/streams/src/handlers/streamCreated.test.ts
StreamCreatedEvent and StreamRecord are defined, STREAM_CREATED_TOPIC and StreamCreatedPayload are added, and parseStreamCreatedPayload() validates the topic and payload fields; tests cover valid parsing and validation failures.
Identity and record mapping
indexer/streams/src/handlers/streamCreated.ts, indexer/streams/src/handlers/streamCreated.test.ts
getEventIdentity() builds a deterministic identity from contract, ledger, transaction hash, and event index, and mapStreamCreatedToRecord() converts payload data and event metadata into a stream record; tests cover identity stability and record field mapping.
Handler composition and exports
indexer/streams/src/handlers/streamCreated.ts, indexer/streams/src/index.ts, indexer/streams/src/handlers/streamCreated.test.ts
handleStreamCreated() combines parsing, mapping, and identity generation, and index.ts re-exports the handler API and types; tests cover the composed return value, repeated-input behavior, and differing stream IDs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hopped through payloads, thump thump thump,
Found stream IDs in a tidy lump.
JSON carrots, crisp and bright,
Identities now line up right.
Ears up! The handler sings tonight 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding the StreamCreated handler.
Description check ✅ Passed The description covers the summary, changes, testing, and verification, though some template sections are omitted.
Linked Issues check ✅ Passed The changes satisfy issue #34 by validating payloads, mapping to records, recording identity, adding idempotent handling, and adding tests.
Out of Scope Changes check ✅ Passed The PR stays within the streams indexer handler, type, index, and test files without introducing unrelated work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@indexer/streams/src/handlers/streamCreated.ts`:
- Around line 22-31: The StreamCreated handler currently trusts a type cast
after JSON.parse, so invalid shapes can still produce a malformed StreamRecord.
Update the logic in streamCreated.ts, around the parsed payload handling in the
StreamCreatedPayload flow, to perform a real runtime validation of the parsed
object before building the return value. Use the existing streamCreated event
handler path to reject or guard against missing or wrong-typed streamId, sender,
recipient, amount, startTime, and endTime fields, and only return the record
once validation passes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc30f585-3708-4e5f-b38f-f39951ab7f30

📥 Commits

Reviewing files that changed from the base of the PR and between bcfa08d and 6e5b3c8.

📒 Files selected for processing (4)
  • indexer/streams/src/handlers/streamCreated.test.ts
  • indexer/streams/src/handlers/streamCreated.ts
  • indexer/streams/src/handlers/types.ts
  • indexer/streams/src/index.ts

Comment thread indexer/streams/src/handlers/streamCreated.ts Outdated
Add runtime validation for all required fields (streamId, sender,
recipient, amount, startTime, endTime) per PR review feedback.
Previously relied solely on TypeScript type assertion which does not
enforce shape at runtime. Now validates field presence, type, and
non-empty constraint with clear error messages.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@indexer/streams/src/handlers/streamCreated.test.ts`:
- Around line 64-99: The negative coverage for parseStreamCreatedPayload is
missing a valid JSON payload with the wrong top-level shape, so add tests in
streamCreated.test for a case like null and ideally another non-object JSON
value. Harden parseStreamCreatedPayload in streamCreated to validate that
JSON.parse returns a non-null object before reading fields, and throw the same
intended validation error instead of letting a raw TypeError escape. Keep the
existing field validation path for streamId, amount, and recipient after the
shape check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78a38b2b-57d1-4a94-b612-8ab650892627

📥 Commits

Reviewing files that changed from the base of the PR and between 6e5b3c8 and 23ce54e.

📒 Files selected for processing (2)
  • indexer/streams/src/handlers/streamCreated.test.ts
  • indexer/streams/src/handlers/streamCreated.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • indexer/streams/src/handlers/streamCreated.ts

Comment on lines +64 to +99
test("throws on invalid JSON data", () => {
const event = createMockEvent({ data: "not-json" });

expect(() => parseStreamCreatedPayload(event)).toThrow(
"Failed to parse event data: invalid JSON",
);
});

test("throws on missing streamId field", () => {
const { streamId: _, ...partial } = mockPayload;
const event = createMockEvent({ data: JSON.stringify(partial) });

expect(() => parseStreamCreatedPayload(event)).toThrow(
'Invalid payload: "streamId" must be a non-empty string',
);
});

test("throws on non-string amount field", () => {
const event = createMockEvent({
data: JSON.stringify({ ...mockPayload, amount: 12345 }),
});

expect(() => parseStreamCreatedPayload(event)).toThrow(
'Invalid payload: "amount" must be a non-empty string',
);
});

test("throws on empty string recipient", () => {
const event = createMockEvent({
data: JSON.stringify({ ...mockPayload, recipient: "" }),
});

expect(() => parseStreamCreatedPayload(event)).toThrow(
'Invalid payload: "recipient" must be a non-empty string',
);
});

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Add a non-object JSON payload case.

These negative tests still miss valid JSON with the wrong top-level shape, e.g. data: "null". In indexer/streams/src/handlers/streamCreated.ts:22-39, JSON.parse would succeed and the subsequent parsed[field] access would throw a raw TypeError instead of the intended validation error. Please add a case for null (and ideally another non-object shape) and harden the parser accordingly.

🤖 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 `@indexer/streams/src/handlers/streamCreated.test.ts` around lines 64 - 99, The
negative coverage for parseStreamCreatedPayload is missing a valid JSON payload
with the wrong top-level shape, so add tests in streamCreated.test for a case
like null and ideally another non-object JSON value. Harden
parseStreamCreatedPayload in streamCreated to validate that JSON.parse returns a
non-null object before reading fields, and throw the same intended validation
error instead of letting a raw TypeError escape. Keep the existing field
validation path for streamId, amount, and recipient after the shape check.

@pragmaticAweds

Copy link
Copy Markdown
Contributor

Hi Chief @d3vobed

Thank you for your awesome contribution, however after analyzing your implementation, there is a minor fixes and merge conflict to be fixed. Kindly fix them to merge your PR asap.

Also do not forget to use fundable.finance to offramp.

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.

Implement StreamCreated handler

2 participants