Skip to content

feat(contracts): add update_start_time with on-chain emission and backend sync (#589) - #656

Open
goodness-cpu wants to merge 1 commit into
ritik4ever:mainfrom
goodness-cpu:update/time
Open

feat(contracts): add update_start_time with on-chain emission and backend sync (#589)#656
goodness-cpu wants to merge 1 commit into
ritik4ever:mainfrom
goodness-cpu:update/time

Conversation

@goodness-cpu

@goodness-cpu goodness-cpu commented Jul 25, 2026

Copy link
Copy Markdown

feat(contracts): add update_start_time with on-chain emission and backend sync

Closes #589

Summary

Implements the missing on-chain counterpart of the existing
start_time_updated event-history type in the backend. The backend already
recorded this event type, but the contract function that should emit it was
never implemented. This PR adds:

  • update_start_time(stream_id, sender, new_start_time) — a new
    contract function with sender-only auth.
  • Validation — rejects canceled streams, requires new_start_time to be
    strictly in the future (relative to the ledger timestamp) and strictly less
    than stream.end_time (matching the issue's acceptance criteria).
  • StreamStartTimeUpdated event — emitted under the existing
    (Stream, StartUp) topic so off-chain listeners (the Soroban event
    indexer) can reconcile the change.
  • Indexer handler — records the event in stream_events with
    oldStartTime and newStartTime metadata so it surfaces in the
    StreamTimeline UI.

Project areas

  • contracts/src/lib.rs — new event struct and function.
  • backend/src/services/indexer.ts — new StartUp case.
  • contracts/src/test.rs — 7 new contract tests covering happy path,
    event payload, sender mismatch, past time, equal-to-end, after-end, and
    canceled stream.

Acceptance criteria

  • Event type start_time_updated recorded in DB after on-chain call.
    The indexer case StartUp calls recordEventWithDb(... "start_time_updated" ...) using INSERT OR IGNORE, which keeps it idempotent across replays.
  • Frontend timeline shows start time update event. Already wired in
    frontend/src/components/StreamTimeline.tsx:
    • FILTER_BUTTONS includes start_time_updated with the 🕐 icon and
      the Start Time Updated label.
    • getEventIcon, formatEventTitle, and getEventDescription all
      handle the event type.
    • eventHistory.ts StreamEventType union already lists the type, so no
      backend type changes were needed.

Contract changes (contracts/src/lib.rs)

#[contracttype]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StreamStartTimeUpdated {
    pub stream_id: u64,
    pub sender: Address,
    pub old_start_time: u64,
    pub new_start_time: u64,
    pub updated_at: u64,
}
pub fn update_start_time(env: Env, stream_id: u64, sender: Address, new_start_time: u64) {
    let mut stream = read_stream(&env, stream_id);
    if stream.sender != sender {
        panic!("sender mismatch");
    }
    sender.require_auth();

    if stream.canceled {
        panic!("stream canceled");
    }

    let now = env.ledger().timestamp();
    if new_start_time <= now {
        panic!("new_start_time must be in the future");
    }
    if new_start_time >= stream.end_time {
        panic!("new_start_time must be before stream end_time");
    }

    let old_start_time = stream.start_time;
    stream.start_time = new_start_time;

    env.storage().persistent().set(&DataKey::Stream(stream_id), &stream);

    env.events().publish(
        (symbol_short!("Stream"), symbol_short!("StartUp")),
        StreamStartTimeUpdated {
            stream_id,
            sender,
            old_start_time,
            new_start_time,
            updated_at: now,
        },
    );
}

Backend changes (backend/src/services/indexer.ts)

case "StartUp":
  recordEventWithDb(
    db,
    value.stream_id.toString(),
    "start_time_updated",
    timestamp,
    value.sender,
    undefined,
    {
      oldStartTime: Number(value.old_start_time),
      newStartTime: Number(value.new_start_time),
    },
    event.ledger,
  );
  break;

Tests added (contracts/src/test.rs)

  1. test_update_start_time_success — scheduled stream update persists.
  2. test_update_start_time_emits_eventStream/StartUp topic with full
    payload (old_start_time, new_start_time, updated_at).
  3. test_update_start_time_fails_with_wrong_sender — sender mismatch panic.
  4. test_update_start_time_rejects_past_timestampnew_start_time <= now
    panic.
  5. test_update_start_time_rejects_at_end_timenew_start_time == end_time panic.
  6. test_update_start_time_rejects_after_end_timenew_start_time > end_time panic.
  7. test_update_start_time_rejects_on_canceled_stream — canceled stream
    panic.

Test run in this PR

  • Contract tests: not runnable in the sandbox (no Rust toolchain). Run
    cd contracts && cargo test
    
    to execute the new tests locally.
  • Backend vitest: pre-existing failures in streamStore.ts
    (elapsed/ratio redeclaration in calculateProgress), webhooks*.test.ts
    (missing stream_events table) and a stale durationSeconds=1 validation
    expectation were NOT introduced by this PR. Targeted runs of the relevant
    suites pass:
    • src/services/eventHistory.test.ts — 11 passed.
    • src/services/indexer.circuitbreaker.test.ts — 5 passed.
    • src/services/streamStore.updateStartAt.test.ts has a pre-existing
      duplicate cacheMocks declaration in the test file itself; fixing that
      in the same area of the repo is recommended but outside the scope of
      this issue.

Known follow-ups (not blocking)

  • The existing updateStreamStartAt in backend/src/services/streamStore.ts
    does a local DB write only and does not submit a Soroban transaction
    invoking the new contract function (unlike createStream/cancelStream
    which do). Wiring that is a small backend-only follow-up — the contract
    contract is now ready to receive such calls.
  • One named event symbol choice would benefit from a clearer name than
    StartUp (e.g. UpdStart) in a future refactor; this PR sticks to the
    existing convention and is non-breaking.

Versioning / migration

  • No new event types in the DB schema (reuses existing start_time_updated
    string in stream_events.event_type).
  • No schema migration required.
  • Frontend unchanged.

How to test locally

# Contract
cd contracts && cargo test update_start_time

# Backend indexer
cd backend && npx vitest run src/services/eventHistory.test.ts
cd backend && npx vitest run src/services/indexer.circuitbreaker.test.ts

Summary by CodeRabbit

  • New Features

    • Stream owners can update a stream’s start time.
    • Start-time changes are recorded with the previous and new timestamps.
  • Bug Fixes

    • Added validation to prevent unauthorized, canceled, past, or post-end-time updates.
  • Tests

    • Added coverage for successful updates, event recording, and invalid update scenarios.

…kend sync (ritik4ever#589)

Implements the missing on-chain counterpart of the existing
backend start_time_updated event history type.

contract (contracts/src/lib.rs):
* Add StreamStartTimeUpdated event struct with stream_id, sender,
  old_start_time, new_start_time, and updated_at fields.
* Add update_start_time(stream_id, sender, new_start_time) function:
  - Sender-only auth (require_auth + sender-mismatch panic, matching
    pause/resume/cancel).
  - Rejects canceled streams.
  - Validates new_start_time > current ledger time
    (new_start_time must be in the future).
  - Validates new_start_time < stream.end_time.
  - Updates storage and emits Stream/StartUp event so off-chain
    listeners can reconcile.

backend (backend/src/services/indexer.ts):
* Add StartUp case to processEvent that records a
  start_time_updated history row with oldStartTime and newStartTime
  metadata, mirroring the resume/pause pattern.

tests (contracts/src/test.rs):
* test_update_start_time_success: scheduled stream update persists
  to storage.
* test_update_start_time_emits_event: asserts Stream+StartUp topic
  and full StreamStartTimeUpdated payload.
* test_update_start_time_fails_with_wrong_sender: sender mismatch
  panics.
* test_update_start_time_rejects_past_timestamp: past-or-equal time
  panics.
* test_update_start_time_rejects_at_end_time: new_start_time ==
  end_time panics.
* test_update_start_time_rejects_after_end_time: new_start_time >
  end_time panics.
* test_update_start_time_rejects_on_canceled_stream: canceled
  stream panics.

Acceptance criteria:
* Event type start_time_updated is recorded in DB once the
  on-chain call lands and the indexer processes the new
  Stream/StartUp event.
* Frontend StreamTimeline already renders start_time_updated
  events with the clock icon and label.

closes ritik4ever#589
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

@goodness-cpu is attempting to deploy a commit to the ritik4ever's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Jul 25, 2026

Copy link
Copy Markdown

@goodness-cpu 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 Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a contract method to update a stream’s start time with validation and event emission, extends contract tests for success and failure cases, and updates the backend indexer to persist the corresponding history event.

Changes

Stream start time updates

Layer / File(s) Summary
Contract update flow
contracts/src/lib.rs, contracts/src/test.rs
Adds StreamStartTimeUpdated and update_start_time, validates sender, cancellation, and time bounds, updates persistent state, emits Stream/StartUp, and tests successful and rejected updates.
Backend event synchronization
backend/src/services/indexer.ts
Handles StartUp events by recording start_time_updated history data with the old and new start times.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: testersweb, bytebinders, jamesvictor-o

Sequence Diagram(s)

sequenceDiagram
  participant update_start_time
  participant processEvent
  participant recordEventWithDb
  update_start_time->>processEvent: Emits Stream/StartUp
  processEvent->>recordEventWithDb: Records start_time_updated with oldStartTime and newStartTime
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly names the new update_start_time contract change and backend sync.
Linked Issues check ✅ Passed The contract method, event emission, backend indexing, and tests address the main requirements of issue #589.
Out of Scope Changes check ✅ Passed All shown changes are directly related to the update_start_time feature and its backend/test support.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
contracts/src/test.rs (2)

2402-2421: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Exercise authentication separately from sender matching.

Because env.mock_all_auths() authorizes every address and the contract checks sender mismatch before sender.require_auth(), this test would still pass if the authorization check were removed. Add a test using the Soroban auth-mocking API that supplies the legitimate sender without granting authorization.

🤖 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 `@contracts/src/test.rs` around lines 2402 - 2421, The
test_update_start_time_fails_with_wrong_sender test currently cannot verify
authentication because mock_all_auths authorizes every address. Replace that
setup with Soroban auth mocking that authorizes only the legitimate sender,
while still invoking update_start_time with wrong_sender; preserve the expected
sender mismatch panic and ensure the test would fail if sender.require_auth()
were removed.

2423-2442: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the exact new_start_time == now boundary.

The implementation rejects new_start_time <= now, but this test only covers a timestamp in the past. Add a case where both values equal 2000 so a regression from <= to < is detected.

🤖 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 `@contracts/src/test.rs` around lines 2423 - 2442, The test
test_update_start_time_rejects_past_timestamp only covers a timestamp earlier
than the current ledger time. Add a separate boundary test, or adjust the setup,
so env.ledger().timestamp and the update_start_time argument both equal 2000,
while retaining the expected “new_start_time must be in the future” panic.
backend/src/services/indexer.ts (1)

300-315: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add an indexer regression test for StartUp.

Add coverage in backend/src/services/indexer.test.ts asserting that the event produces a start_time_updated row with the sender, ledger sequence, and both old/new metadata fields.

🤖 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 `@backend/src/services/indexer.ts` around lines 300 - 315, Add a regression
test in indexer.test.ts for the StartUp handling path, invoking the indexer with
representative startup data and asserting that a start_time_updated row is
created with the expected sender, ledger sequence, oldStartTime, and
newStartTime values.
🤖 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 `@backend/src/services/indexer.ts`:
- Around line 300-315: Update the StartUp handling in recordEventWithDb so
value.old_start_time and value.new_start_time are preserved exactly instead of
being coerced with Number(...). Store them as decimal strings or the
repository’s established exact u64 representation, and update any affected
consumers or callers to handle that representation without precision loss.

---

Nitpick comments:
In `@backend/src/services/indexer.ts`:
- Around line 300-315: Add a regression test in indexer.test.ts for the StartUp
handling path, invoking the indexer with representative startup data and
asserting that a start_time_updated row is created with the expected sender,
ledger sequence, oldStartTime, and newStartTime values.

In `@contracts/src/test.rs`:
- Around line 2402-2421: The test_update_start_time_fails_with_wrong_sender test
currently cannot verify authentication because mock_all_auths authorizes every
address. Replace that setup with Soroban auth mocking that authorizes only the
legitimate sender, while still invoking update_start_time with wrong_sender;
preserve the expected sender mismatch panic and ensure the test would fail if
sender.require_auth() were removed.
- Around line 2423-2442: The test test_update_start_time_rejects_past_timestamp
only covers a timestamp earlier than the current ledger time. Add a separate
boundary test, or adjust the setup, so env.ledger().timestamp and the
update_start_time argument both equal 2000, while retaining the expected
“new_start_time must be in the future” panic.
🪄 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 Plus

Run ID: 5da8aa07-0693-4db2-8d93-69fe5f21e104

📥 Commits

Reviewing files that changed from the base of the PR and between b3d32c1 and 0225e64.

📒 Files selected for processing (3)
  • backend/src/services/indexer.ts
  • contracts/src/lib.rs
  • contracts/src/test.rs

Comment on lines +300 to +315

case "StartUp":
recordEventWithDb(
db,
value.stream_id.toString(),
"start_time_updated",
timestamp,
value.sender,
undefined,
{
oldStartTime: Number(value.old_start_time),
newStartTime: Number(value.new_start_time),
},
event.ledger,
);
break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate indexer and schemas =="
fd -a 'indexer\.ts|schemas\.ts$' . | sed 's#^./##'

echo "== relevant indexer section =="
idx="$(fd 'indexer\.ts$' . | head -n1)"
if [ -n "${idx:-}" ]; then
  wc -l "$idx"
  sed -n '1,70p;230,340p' "$idx" | cat -n
fi

echo "== event record implementation =="
rg -n "recordEventWithDb|function recordEventWithDb|record.*Event|event.*metadata|JSON\.stringify|oldStartTime|newStartTime|start_time_updated" -S .

echo "== probe u64 Number.MAX_SAFE_INTEGER issue =="
node - <<'JS'
const values = [
  BigInt(String(Number.MAX_SAFE_INTEGER)),
  BigInt(String(Number.MAX_SAFE_INTEGER + 1)),
  18446744073709551615n
];
for (const value of values) {
  const num = Number(value);
  const back = BigInt.asIntN(64, BigInt(num));
  console.log(JSON.stringify({
    bigint: value.toString(),
    number: num,
    backToBigInt: back.toString(),
    exact: value.toString() === back.toString(),
    aboveSafe: value > BigInt(Number.MAX_SAFE_INTEGER),
  }));
}
JS

Repository: ritik4ever/stellar-stream

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'indexer\.ts$' .
fd -a 'schemas\.ts$' .

echo "== target file lines =="
idx="$(fd 'indexer\.ts$' . | head -n1)"
if [ -n "${idx:-}" ]; then
  echo "INDEXER=$idx"
  wc -l "$idx"
  sed -n '1,90p;240,330p' "$idx" | nl -ba
fi

echo "== focused event/event metadata functions and schema usages =="
rg -n "recordEventWithDb|function recordEventWithDb|const recordEventWithDb|StartUp|start_time_updated|oldStartTime|newStartTime|value\.old_start_time|value\.new_start_time|JSON\.stringify|RecordEvent" -S "$idx" $(fd 'schemas\.ts$' . | head -n5 || true)

echo "== Node u64 Number precision probe =="
node - <<'JS'
const values = [
  BigInt(String(Number.MAX_SAFE_INTEGER)),
  BigInt(String(Number.MAX_SAFE_INTEGER) + "+1"),
  18446744073709551615n
];
for (const value of values) {
  const num = Number(value);
  const back = BigInt(num) & ((1n << 64n) - 1n);
  console.log(JSON.stringify({
    bigint: value.toString(),
    number: num,
    backThroughBigIntNumber: back.toString(),
    exact: value.toString() === back.toString(),
    aboveSafe: value > BigInt(Number.MAX_SAFE_INTEGER),
  }));
}
JS

Repository: ritik4ever/stellar-stream

Length of output: 488


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target file lines =="
sed -n '1,90p;240,330pM' backend/src/services/indexer.ts | awk '{printf "%6d\t%s\n", NR+0, $0}'

echo "== focused event/event metadata functions and schema usages =="
rg -n "recordEventWithDb|function recordEventWithDb|const recordEventWithDb|StartUp|start_time_updated|oldStartTime|newStartTime|value\.old_start_time|value\.new_start_time|JSON\.stringify|RecordEvent" -S backend/src/services/indexer.ts backend/src/validation/schemas.ts

echo "== Node u64 Number precision probe =="
node - <<'JS'
const values = [
  BigInt(String(Number.MAX_SAFE_INTEGER)),
  BigInt(String(Number.MAX_SAFE_INTEGER) + "+1"),
  18446744073709551615n
];
for (const value of values) {
  const num = Number(value);
  const back = BigInt(num) & ((1n << 64n) - 1n);
  console.log(JSON.stringify({
    bigint: value.toString(),
    number: num,
    backThroughBigIntNumber: back.toString(),
    exact: value.toString() === back.toString(),
    aboveSafe: value > BigInt(Number.MAX_SAFE_INTEGER),
  }));
}
JS

Repository: ritik4ever/stellar-stream

Length of output: 250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target file lines =="
sed -n '1,90p;240,330p' backend/src/services/indexer.ts | awk '{printf "%6d\t%s\n", NR+0, $0}'

echo "== focused event/event metadata functions and schema usages =="
rg -n "recordEventWithDb|function recordEventWithDb|const recordEventWithDb|StartUp|start_time_updated|oldStartTime|newStartTime|value\.old_start_time|value\.new_start_time|JSON\.stringify|RecordEvent" -S backend/src/services/indexer.ts backend/src/validation/schemas.ts

echo "== Node u64 Number precision probe =="
node - <<'JS'
const values = [
  BigInt(String(Number.MAX_SAFE_INTEGER)),
  BigInt(String(Number.MAX_SAFE_INTEGER) + "+1"),
  18446744073709551615n
];
for (const value of values) {
  const num = Number(value);
  const back = BigInt(num) & ((1n << 64n) - 1n);
  console.log(JSON.stringify({
    bigint: value.toString(),
    number: num,
    backThroughBigIntNumber: back.toString(),
    exact: value.toString() === back.toString(),
    aboveSafe: value > BigInt(Number.MAX_SAFE_INTEGER),
  }));
}
JS

Repository: ritik4ever/stellar-stream

Length of output: 7429


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== eventHistory related files =="
fd -a 'eventHistory\.ts$' backend/src/services

echo "== eventHistory implementation =="
evt="$(fd 'eventHistory\.ts$' backend/src/services | head -n1)"
if [ -n "${evt:-}" ]; then
  wc -l "$evt"
  sed -n '1,220p' "$evt" | awk '{printf "%6d\t%s\n", NR+0, $0}'
fi

echo "== backend validation schema event rows =="
rg -n "event_history|start_time_updated|oldStartTime|newStartTime|JSON|metadata" -S backend/src backend/src/services backend/src/validation || true

echo "== Node u64 Number precision probe =="
node - <<'JS'
const values = [
  BigInt(String(Number.MAX_SAFE_INTEGER)),
  BigInt(number = Number.MAX_SAFE_INTEGER + 1),
  18446744073709551615n
];
for (const value of values) {
  const num = Number(value);
  const back = BigInt(num) & ((1n << 64n) - 1n);
  console.log(JSON.stringify({
    bigint: value.toString(),
    number: num,
    backThroughBigIntNumber: back.toString(),
    exact: value.toString() === back.toString(),
    aboveSafe: value > BigInt(Number.MAX_SAFE_INTEGER),
  }));
}
JS

Repository: ritik4ever/stellar-stream

Length of output: 27723


Do not coerce on-chain u64 timestamps to Number.

Valid timestamps above Number.MAX_SAFE_INTEGER lose precision when passed through Number(...) before JSON.stringify, so oldStartTime/newStartTime can be corrupted in persisted event metadata. Store these values as decimal strings or another exact representation and update callers accordingly.

Preserve exact timestamp values
-            oldStartTime: Number(value.old_start_time),
-            newStartTime: Number(value.new_start_time),
+            oldStartTime: String(value.old_start_time),
+            newStartTime: String(value.new_start_time),
📝 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
case "StartUp":
recordEventWithDb(
db,
value.stream_id.toString(),
"start_time_updated",
timestamp,
value.sender,
undefined,
{
oldStartTime: Number(value.old_start_time),
newStartTime: Number(value.new_start_time),
},
event.ledger,
);
break;
case "StartUp":
recordEventWithDb(
db,
value.stream_id.toString(),
"start_time_updated",
timestamp,
value.sender,
undefined,
{
oldStartTime: String(value.old_start_time),
newStartTime: String(value.new_start_time),
},
event.ledger,
);
break;
🤖 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 `@backend/src/services/indexer.ts` around lines 300 - 315, Update the StartUp
handling in recordEventWithDb so value.old_start_time and value.new_start_time
are preserved exactly instead of being coerced with Number(...). Store them as
decimal strings or the repository’s established exact u64 representation, and
update any affected consumers or callers to handle that representation without
precision loss.

@ritik4ever

Copy link
Copy Markdown
Owner

Hi @goodness-cpu,

This PR could not be merged because it has merge conflicts with the target branch.

Please resolve the merge conflicts, push the updated changes, and the PR can be reviewed and merged.

Thank you!

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.

[FEATURE] Add update_start_time function to contract with backend sync

2 participants