Skip to content

Replace Faucet fixed-sleep settle with eth_getBalance poll loop#1

Merged
e-fu merged 2 commits into
mainfrom
task-6-faucet-poll-loop
May 15, 2026
Merged

Replace Faucet fixed-sleep settle with eth_getBalance poll loop#1
e-fu merged 2 commits into
mainfrom
task-6-faucet-poll-loop

Conversation

@e-fu
Copy link
Copy Markdown
Contributor

@e-fu e-fu commented May 15, 2026

Summary

Replaces the fixed 2.5 s Process.sleep in Onchain.Tempo.Faucet.fresh_funded_wallet/1 with a poll loop on eth_getBalance. The helper now returns as soon as the funding transaction lands on-chain — ~one block (~500 ms on Moderato) instead of a flat ~2.5 s.

Closes Task 6 ([D:3/B:3/U:3 → Eff:1.0]).

Option semantics

  • :settle_ms is now the poll timeout (default 10_000 ms) — previously the sleep duration (2_500 ms).
  • settle_ms: 0 still skips the wait entirely (unit tests rely on this).
  • New :poll_interval_ms (default 200 ms) tunes the poll cadence.

No call-site changes required — existing unit tests pass settle_ms: 0 and integration tests pass nothing (using the new default).

Test plan

  • mix test.json --quiet test/onchain/tempo/faucet_test.exs — all 10 tests pass, including 2 new ones (poll-success, poll-timeout)
  • mix format --check-formatted — clean
  • mix credo --strict — 0 issues
  • mix dialyzer.json — 0 warnings
  • mix doctor — 100% doc/spec coverage, validation passed
  • Faucet module coverage 88.1% (above 80% standard tier)
  • Integration suite verification neededTEMPO_RPC_URL=https://rpc.moderato.tempo.xyz mix test.json --include integration should drop wall-clock noticeably (was ≥7.5 s in setup sleeps; expect ~1.5–3 s now, dominated by 3× actual block confirmation)

Summary by CodeRabbit

  • New Features

    • Wallet funding now polls until on-chain balance is observed, with configurable poll interval and a timeout option (:settle_ms, :poll_interval_ms).
  • Chores

    • CHANGELOG and roadmap updated to reflect the faucet behavior and task status change.
  • Tests

    • Added tests for polling-until-funded, timeout handling, and input validation of wait/poll options.

Review Change Stack

Copilot AI review requested due to automatic review settings May 15, 2026 09:59
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

📝 Walkthrough

Walkthrough

This PR replaces fixed-delay settlement in fresh_funded_wallet/1 with active polling of eth_getBalance until the wallet is funded, bounded by a configurable timeout. The polling interval and timeout are user-configurable, and the change is covered by tests for success, timeout, and input validation.

Changes

Polling-based wallet settlement

Layer / File(s) Summary
Polling contract and documentation
lib/onchain/tempo/faucet.ex, CHANGELOG.md
Module docs and attributes now describe :settle_ms (default 10_000, 0 skips waiting) and :poll_interval_ms (default 200), replacing the prior fixed-sleep default; changelog documents the switch to polling.
Polling loop implementation
lib/onchain/tempo/faucet.ex
fresh_funded_wallet/1 funds the generated address then validates wait options and calls wait_for_funding/2. New helpers implement deadline-bounded polling (poll_balance/6) and parse_balance_hex/1 to parse eth_getBalance hex results.
Polling behavior tests
test/onchain/tempo/faucet_test.exs
Tests added: successful polling until funding lands; timeout error when funding never confirms; validation of negative/non-integer :settle_ms; validation of non-positive/negative :poll_interval_ms; and skipping poll when settle_ms: 0 even if :poll_interval_ms invalid.
Roadmap task completion tracking
ROADMAP.md, roadmap/data.json, roadmap/tasks.toml
Task 6 (faucet_polish) status changed from pending to done across roadmap metadata files.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code where faucets sleep no more,
I poll the chain and listen at the door,
Timeouts keep me patient, intervals keep me true,
Fresh wallets wake when tiny balances accrue,
I nibble carrots and logs — the tests all chew.

🚥 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 directly and clearly summarizes the main change: replacing fixed-sleep settlement with eth_getBalance polling in the Faucet function.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task-6-faucet-poll-loop

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Replaces the fixed 2.5 s Process.sleep in Onchain.Tempo.Faucet.fresh_funded_wallet/1 with an eth_getBalance poll loop that returns as soon as the funding transaction lands on-chain. :settle_ms is repurposed as the poll timeout (default 10_000 ms), and a new :poll_interval_ms option (default 200 ms) controls the cadence. Roadmap/changelog metadata is updated accordingly.

Changes:

  • Refactor fresh_funded_wallet/1 to use with + wait_for_funding/2 that polls eth_getBalance until non-zero or deadline reached.
  • Update module/option docstrings and add two new unit tests (poll-success and poll-timeout) using Req.Test stubs and :counters.
  • Mark Task 6 done in roadmap/tasks.toml, roadmap/data.json, ROADMAP.md; add a CHANGELOG entry.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
lib/onchain/tempo/faucet.ex Replaces fixed sleep with bounded eth_getBalance poll loop; adds parse_balance_hex/1 and poll_balance/6; updates docs.
test/onchain/tempo/faucet_test.exs Adds two tests covering successful polling and timeout behavior.
roadmap/tasks.toml Marks Task 6 status as done.
roadmap/data.json Mirrors Task 6 status update.
ROADMAP.md Flips Task 6 row to ✅.
CHANGELOG.md Documents the new polling behavior and option semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@lib/onchain/tempo/faucet.ex`:
- Around line 103-112: The code currently reads :settle_ms into timeout_ms and
:poll_interval_ms into interval_ms then calls poll_balance(addr_hex, url,
rpc_opts, deadline, interval_ms, timeout_ms) without validating bounds; add
input validation at the start of the function that reads opts (before computing
deadline/poll_balance): ensure :settle_ms (timeout_ms) is an integer >= 0 and
:poll_interval_ms (interval_ms) is an integer > 0 when timeout_ms > 0, and
return {:error, {:invalid_option, reason}} (or similar tuple used in this
module) for invalid values instead of entering the loop; keep existing defaults
( `@default_wait_timeout_ms` and `@default_poll_interval_ms` ) when keys are absent
and only validate after defaults are applied, and reference the existing symbols
timeout_ms, interval_ms, poll_balance, and rpc_url when updating the function.

In `@roadmap/data.json`:
- Line 170: Record for "task 6" is marked "status": "done" but is missing the
done_at timestamp and still has phases.3.status set to "pending"; update the
task entry by adding a proper ISO 8601 done_at datetime and set phases.3.status
to "done" (or the appropriate completed status) so the task-level and
phase-level rollups are consistent—look for the task object labelled "task 6",
the "status" field, the "done_at" field, and the "phases.3.status" property to
apply the updates.

In `@roadmap/tasks.toml`:
- Line 135: The roadmap task metadata only sets status = "done" but lacks the
completion timestamp and a matching phase status; update the same task block to
add a done_at field with an ISO 8601 timestamp (e.g. 2026-05-15T12:00:00Z) and
set phases.3.status (the third phase entry for this task) to "done" so the phase
state aligns with the task status (refer to the task block containing status =
"done" and the phases array).
🪄 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: 63519758-fb96-42c2-8bb8-9addb1e5fd39

📥 Commits

Reviewing files that changed from the base of the PR and between d5e7dd2 and abcf1c7.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • ROADMAP.md
  • lib/onchain/tempo/faucet.ex
  • roadmap/data.json
  • roadmap/tasks.toml
  • test/onchain/tempo/faucet_test.exs

Comment thread lib/onchain/tempo/faucet.ex
Comment thread roadmap/data.json
"phase": 3,
"bundle": "faucet_polish",
"status": "pending",
"status": "done",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Backfill completion metadata when switching task 6 to done.

Task 6 is now done, but this record still lacks done_at, and phases.3.status is still pending. That leaves roadmap rollups internally inconsistent in this file.

Suggested patch
@@
     "3": {
       "name": "Future Work",
       "order": 3,
-      "status": "pending"
+      "status": "done"
     }
@@
       "id": 6,
       "phase": 3,
       "bundle": "faucet_polish",
       "status": "done",
+      "done_at": "2026-05-15",
       "title": "Replace Faucet fixed-sleep settle with poll loop",
📝 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
"status": "done",
"3": {
"name": "Future Work",
"order": 3,
"status": "done"
}
"id": 6,
"phase": 3,
"bundle": "faucet_polish",
"status": "done",
"done_at": "2026-05-15",
"title": "Replace Faucet fixed-sleep settle with poll loop",
🤖 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 `@roadmap/data.json` at line 170, Record for "task 6" is marked "status":
"done" but is missing the done_at timestamp and still has phases.3.status set to
"pending"; update the task entry by adding a proper ISO 8601 done_at datetime
and set phases.3.status to "done" (or the appropriate completed status) so the
task-level and phase-level rollups are consistent—look for the task object
labelled "task 6", the "status" field, the "done_at" field, and the
"phases.3.status" property to apply the updates.

Comment thread roadmap/tasks.toml
phase = 3
bundle = "faucet_polish"
status = "pending"
status = "done"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep TOML roadmap metadata consistent with the new done status.

After marking task 6 as done, this block should also include done_at, and phases.3.status should be updated to match the now-completed phase state.

Suggested patch
@@
 [phases.3]
 name = "Future Work"
 order = 3
-status = "pending"
+status = "done"
@@
 [[task]]
 id = 6
 phase = 3
 bundle = "faucet_polish"
 status = "done"
+done_at = "2026-05-15"
 title = "Replace Faucet fixed-sleep settle with poll loop"
🤖 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 `@roadmap/tasks.toml` at line 135, The roadmap task metadata only sets status =
"done" but lacks the completion timestamp and a matching phase status; update
the same task block to add a done_at field with an ISO 8601 timestamp (e.g.
2026-05-15T12:00:00Z) and set phases.3.status (the third phase entry for this
task) to "done" so the phase state aligns with the task status (refer to the
task block containing status = "done" and the phases array).

@e-fu e-fu mentioned this pull request May 15, 2026
7 tasks
@e-fu e-fu force-pushed the task-6-faucet-poll-loop branch from abcf1c7 to 550c341 Compare May 15, 2026 10:21
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/onchain/tempo/faucet_test.exs (1)

203-207: ⚡ Quick win

Assert the “no RPC” contract explicitly.

Line 203’s test name claims fail-fast with no RPC, but it currently only checks the returned error text. Add a stub + refute_received so the behavior is truly enforced.

Proposed test tightening
 test "rejects negative :settle_ms with actionable error (fails fast, no RPC)" do
-  assert {:error, msg} = Faucet.fresh_funded_wallet(settle_ms: -1)
+  test_pid = self()
+
+  Req.Test.stub(:should_not_call, fn conn ->
+    send(test_pid, :rpc_called)
+    Req.Test.json(conn, %{"jsonrpc" => "2.0", "id" => 1, "result" => ["0xfund"]})
+  end)
+
+  assert {:error, msg} =
+           Faucet.fresh_funded_wallet(
+             rpc_url: "http://localhost",
+             req_options: [plug: {Req.Test, :should_not_call}],
+             settle_ms: -1
+           )
+
+  refute_received :rpc_called
   assert msg =~ ":settle_ms must be a non-negative integer"
   assert msg =~ "-1"
 end
🤖 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 `@test/onchain/tempo/faucet_test.exs` around lines 203 - 207, The test should
also assert that no RPC is invoked when Faucet.fresh_funded_wallet(settle_ms:
-1) fails fast: add a test-local stub/mock for the RPC client used by Faucet
(the module Faucet calls for external RPCs) and set its expectation to zero
calls, then after calling Faucet.fresh_funded_wallet assert refute_received (or
verify the mock had 0 invocations) to ensure no RPC messages were sent; keep the
existing assertions on the error message intact and reference
Faucet.fresh_funded_wallet in the updated test name/description.
🤖 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.

Nitpick comments:
In `@test/onchain/tempo/faucet_test.exs`:
- Around line 203-207: The test should also assert that no RPC is invoked when
Faucet.fresh_funded_wallet(settle_ms: -1) fails fast: add a test-local stub/mock
for the RPC client used by Faucet (the module Faucet calls for external RPCs)
and set its expectation to zero calls, then after calling
Faucet.fresh_funded_wallet assert refute_received (or verify the mock had 0
invocations) to ensure no RPC messages were sent; keep the existing assertions
on the error message intact and reference Faucet.fresh_funded_wallet in the
updated test name/description.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 68ceed3b-eb31-4aae-96fe-5b76bc5e597d

📥 Commits

Reviewing files that changed from the base of the PR and between 550c341 and df1a9e3.

📒 Files selected for processing (2)
  • lib/onchain/tempo/faucet.ex
  • test/onchain/tempo/faucet_test.exs
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/onchain/tempo/faucet.ex

@e-fu e-fu merged commit ba785a4 into main May 15, 2026
2 checks passed
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.

2 participants