allow adding an end date to an existing stream without one#1844
allow adding an end date to an existing stream without one#1844
Conversation
There was a problem hiding this comment.
Pull request overview
Adds UI + transaction-building support to retroactively set an end date on an existing stream that previously had no end date, by computing a duration relative to an explicit start timestamp.
Changes:
- Extends the edit-stream flow UI/state to optionally collect an end date/time (UTC) and submit it.
- Updates
buildEditStreamBatchto (optionally) compute a newduration(and start) and include it in thesetStreamscall. - Minor UI layering tweak on the stream details page (
z-[1]).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
src/routes/(pages)/app/(app)/[accountId]/tokens/[token]/streams/[dripId]/+page.svelte |
Adjusts z-index class on a stream timeline block. |
src/lib/utils/streams/streams.ts |
Adds end-date editing inputs to buildEditStreamBatch, computes duration, and updates receiver config/metadata accordingly. |
src/lib/flows/edit-stream-flow/enter-new-details.svelte |
Adds toggleable “Add end date” UI, validation, and wires new fields into the edit transaction flow. |
src/lib/flows/edit-stream-flow/edit-stream-flow-state.ts |
Adds state fields for end-date toggle + date/time inputs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const { newHash, batch } = await buildEditStreamBatch(stream.id, { | ||
| name: nameUpdated ? $context.newName : undefined, | ||
| amountPerSecond: amountUpdated ? newAmountPerSecond : undefined, | ||
| newEndDate: endDateUpdated ? combinedEndDate : undefined, | ||
| actualStartDate: endDateUpdated ? actualStartDate : undefined, |
There was a problem hiding this comment.
name is passed through as-is when updated. If the input is cleared, $context.newName will be an empty string; combined with buildEditStreamBatch's truthy checks, this can produce an empty batch (and still enable the confirm button via nameUpdated). Normalize the value before sending (e.g., trim and convert '' to undefined) and align nameUpdated with the normalized value so the flow doesn't allow submitting a no-op.
| startSeconds = BigInt(Math.floor(newData.actualStartDate.getTime() / 1000)); | ||
| const endDateSeconds = Math.floor(newData.newEndDate.getTime() / 1000); | ||
| durationSeconds = endDateSeconds - Number(startSeconds); | ||
| assert(durationSeconds > 0, 'Duration must be positive'); |
There was a problem hiding this comment.
durationSeconds/startSeconds are encoded into 32-bit fields in streamConfigToUint256 (see stream-config-utils.ts), but this code doesn't validate the computed values fit uint32. A user can enter a far-future end date (e.g. year 9999) and cause streamConfigToUint256 to throw (via its internal round-trip/unreachable checks). Add explicit range validation for both startSeconds and the computed duration (0 < duration <= 0xFFFF_FFFF, start <= 0xFFFF_FFFF), and consider computing the duration using BigInt arithmetic to avoid Number(...) conversions.
| startSeconds = BigInt(Math.floor(newData.actualStartDate.getTime() / 1000)); | |
| const endDateSeconds = Math.floor(newData.newEndDate.getTime() / 1000); | |
| durationSeconds = endDateSeconds - Number(startSeconds); | |
| assert(durationSeconds > 0, 'Duration must be positive'); | |
| const UINT32_MAX = 0xFFFF_FFFFn; | |
| const startSecondsBigInt = BigInt(Math.floor(newData.actualStartDate.getTime() / 1000)); | |
| const endSecondsBigInt = BigInt(Math.floor(newData.newEndDate.getTime() / 1000)); | |
| const durationBigInt = endSecondsBigInt - startSecondsBigInt; | |
| assert(durationBigInt > 0n, 'Duration must be positive'); | |
| assert(startSecondsBigInt <= UINT32_MAX, 'Start time is too far in the future'); | |
| assert(durationBigInt <= UINT32_MAX, 'Duration exceeds maximum allowed value'); | |
| startSeconds = startSecondsBigInt; | |
| durationSeconds = Number(durationBigInt); |
| if (newData.name || durationSeconds !== undefined) { | ||
| const metadata = _buildMetadata(currentStreams, ownAccountId); |
There was a problem hiding this comment.
These conditionals treat name as truthy/falsy (if (newData.name ...)). If the UI passes an empty string (common when clearing an optional text input), this block is skipped and buildEditStreamBatch can return an empty batch, which will later result in attempting to submit a no-op/invalid transaction. Use explicit undefined checks (newData.name !== undefined) and decide how to handle empty-string names (normalize to undefined or allow clearing by deleting the name field in metadata).
Adds the ability to retroactively add an end date to a stream that doesn't yet have one.
To align with the contract logic, it sets the start date when the stream happened to be actually confirmed as the new explicit
startDate, and then adjustsduration(seconds) relative to that new explicit startDate in order to achieve the new intended end date.