Skip to content

Conversation

sudo-shashank
Copy link
Contributor

@sudo-shashank sudo-shashank commented Oct 13, 2025

Summary of changes

Changes introduced in this pull request:

  • Check for low faucet balance and return error.
  • Updated api doc

Preview URL: https://1fac3c2.forest-explorer-preview.workers.dev

Reference issue to close (if applicable)

Closes

Other information and links

Change checklist

  • I have performed a self-review of my own code,
  • I have made corresponding changes to the documentation. All new code
    adheres to the team's
    documentation standards,
  • I have added tests that prove my fix is effective or that my feature works
    (if possible),
  • I have made sure the CHANGELOG is up-to-date. All user-facing changes
    should be reflected in this document.

Summary by CodeRabbit

  • New Features

    • Added a pre-check during faucet token claims to verify available balance. If insufficient, the request is stopped and a clear error message (“Faucet is empty, Request top-up”) is returned, improving user feedback and avoiding unnecessary processing.
  • Documentation

    • Introduced a “Faucet Top-Up Requests” section explaining behavior when the faucet is exhausted, including an example error and a link to request a top-up.

Copy link
Contributor

coderabbitai bot commented Oct 13, 2025

Walkthrough

Introduces a documentation section on faucet top-up requests and modifies claim_token in the faucet server to pre-check faucet balance via rpc.wallet_balance. If balance is below drip_amount, it returns a specific ServerError and stops; otherwise, it proceeds with existing claim handling.

Changes

Cohort / File(s) Summary of Changes
Documentation
docs/api-documentation.md
Added "Faucet Top-Up Requests" section describing behavior when faucet is exhausted, including example error and top-up request link.
Faucet Server API
src/faucet/server_api.rs
Added pre-check in claim_token: calls rpc.wallet_balance and returns ServerError ("Faucet is empty, Request top-up") if balance < drip_amount; otherwise continues existing logic.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Faucet Server (claim_token)
    participant R as RPC (wallet_balance)
    participant F as Faucet Logic

    C->>S: POST /claim_token
    S->>R: wallet_balance(faucet_address)
    R-->>S: balance
    alt Balance < drip_amount
        S-->>C: Error: "Faucet is empty, Request top-up"
    else Balance >= drip_amount
        S->>F: Proceed with existing faucet_info flow
        F-->>S: Result (success/failure)
        S-->>C: Response (as before)
    end

    note over S: New pre-check inserted before prior logic
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • elmattic
  • akaladarshi

Poem

A thimble of drops in the faucet’s cup,
I twitch my ears—uh oh, it’s dried up!
Ping the balance, pause the hop;
If it’s empty, leave a top-up prop.
When coins return, I’ll gladly stream—
Drip by drip, a bunny’s dream. 🐇💧

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly summarizes the primary change by indicating that the claim token API now handles scenarios where the faucet balance is low. It is concise, clear, and focused on the key update without extraneous details.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch shashank/claim-api-balance-check

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

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.23%. Comparing base (37b05f1) to head (1fac3c2).

Files with missing lines Patch % Lines
src/faucet/server_api.rs 0.00% 9 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #327      +/-   ##
==========================================
- Coverage   39.37%   39.23%   -0.14%     
==========================================
  Files          40       40              
  Lines        2537     2546       +9     
==========================================
  Hits          999      999              
- Misses       1538     1547       +9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sudo-shashank sudo-shashank marked this pull request as ready for review October 13, 2025 13:25
@sudo-shashank sudo-shashank requested a review from a team as a code owner October 13, 2025 13:25
Copy link
Contributor

@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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37b05f1 and 1fac3c2.

📒 Files selected for processing (2)
  • docs/api-documentation.md (1 hunks)
  • src/faucet/server_api.rs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/faucet/server_api.rs (2)
src/utils/rpc_context.rs (2)
  • new (42-59)
  • new (111-113)
src/faucet/controller.rs (1)
  • new (24-85)
🪛 markdownlint-cli2 (0.18.1)
docs/api-documentation.md

157-157: Link text should be descriptive

(MD059, descriptive-link-text)

Comment on lines +146 to +158
## Faucet Top-Up Requests
If you encounter a server error indicating that faucet is exhausted.
**Example:**
```bash
ServerError|Faucet is empty, Request top-up
```
You can request for faucet top-up
[Here](https://github.com/ChainSafe/forest-explorer/discussions/134). This
discussion thread is monitored for top-up requests.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Improve grammar and link text descriptiveness.

Two minor issues in this documentation section:

  • Line 148: "that faucet is exhausted" should be "that the faucet is exhausted"
  • Lines 156-158: The link text "Here" is not descriptive. Consider rewording to something like: "You can request a faucet top-up in this discussion thread."

Apply this diff:

-If you encounter a server error indicating that faucet is exhausted.
+If you encounter a server error indicating that the faucet is exhausted.

-You can request for faucet top-up
-[Here](https://github.com/ChainSafe/forest-explorer/discussions/134). This
-discussion thread is monitored for top-up requests.
+You can [request a faucet top-up](https://github.com/ChainSafe/forest-explorer/discussions/134) 
+in this discussion thread, which is monitored for top-up requests.

Based on learnings

📝 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
## Faucet Top-Up Requests
If you encounter a server error indicating that faucet is exhausted.
**Example:**
```bash
ServerError|Faucet is empty, Request top-up
```
You can request for faucet top-up
[Here](https://github.com/ChainSafe/forest-explorer/discussions/134). This
discussion thread is monitored for top-up requests.
## Faucet Top-Up Requests
If you encounter a server error indicating that the faucet is exhausted.
**Example:**
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

157-157: Link text should be descriptive

(MD059, descriptive-link-text)

🤖 Prompt for AI Agents
In docs/api-documentation.md around lines 146 to 158, fix two doc issues: change
"that faucet is exhausted" to "that the faucet is exhausted" on line 148, and
replace the non-descriptive link text "Here" (lines 156-158) with more
descriptive inline text such as "request a faucet top-up" linking to
https://github.com/ChainSafe/forest-explorer/discussions/134 so the sentence
reads like: "You can request a faucet top-up in this discussion thread."

Comment on lines +222 to +230
let faucet_balance = rpc
.wallet_balance(from, &faucet_info.token_type())
.await
.map_err(ServerFnError::new)?;
if faucet_balance < *faucet_info.drip_amount() {
return Err(ServerFnError::ServerError(
"Faucet is empty, Request top-up".to_string(),
));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Missing gas balance check for ERC-20 faucets and TOCTOU consideration.

Two concerns with the balance pre-check:

  1. Major issue: For ERC-20 faucets, this check verifies the ERC-20 token balance but does not verify that the faucet has sufficient native tokens to pay for gas. A faucet could have adequate ERC-20 tokens but insufficient native tokens for gas, leading to transaction failures after rate-limit tracking.

  2. TOCTOU consideration: The balance is checked before the transfer executes. Between checking and transferring, concurrent requests could deplete the faucet, causing on-chain transaction failures. While the blockchain will serialize transactions and the impact is limited (clearer error messages, minimal gas waste), this creates a race window where users bypass the early check.

Recommended fix for the major issue:

For ERC-20 faucets, add an additional balance check for native tokens needed for gas:

 let faucet_balance = rpc
     .wallet_balance(from, &faucet_info.token_type())
     .await
     .map_err(ServerFnError::new)?;
 if faucet_balance < *faucet_info.drip_amount() {
     return Err(ServerFnError::ServerError(
         "Faucet is empty, Request top-up".to_string(),
     ));
 }
+
+// For ERC-20 faucets, also check native token balance for gas
+if matches!(faucet_info.token_type(), TokenType::Erc20(_)) {
+    let native_balance = rpc
+        .wallet_balance(from, &TokenType::Native)
+        .await
+        .map_err(ServerFnError::new)?;
+    // Rough estimate: gas_limit * max_gas_price
+    let estimated_gas_cost = TokenAmount::from_atto(
+        faucet_info.max_gas_limit() as u128 * 2_000_000_000u128 // 2 Gwei estimate
+    );
+    if native_balance < estimated_gas_cost {
+        return Err(ServerFnError::ServerError(
+            "Faucet has insufficient native tokens for gas fees, Request top-up".to_string(),
+        ));
+    }
+}
+
 match faucet_info {

For the TOCTOU concern:

The fail-fast approach is valuable for UX despite the race window. If you want to minimize on-chain failures and gas waste, consider implementing advisory locking (e.g., using Redis with SETNX or a database row lock) around the balance check and transfer operation. However, given the limited impact and the complexity of distributed locking, the current approach may be acceptable. Please verify whether on-chain transaction failures due to insufficient balance are an acceptable trade-off.


🏁 Script executed:

#!/bin/bash
# Find the definition of wallet_balance
rg -n 'fn wallet_balance' -C3 --type rust

# Find the TokenType enum definition
rg -n 'enum TokenType' -C5 --type rust

# Show lines 200–260 in server_api.rs around the balance check
sed -n '200,260p' src/faucet/server_api.rs

Length of output: 4119


🏁 Script executed:

#!/bin/bash
# Find the ERC-20 claim handler
rg -n 'handle_erc20_claim' -C5 --type rust

# Find the native claim handler
rg -n 'handle_native_claim' -C5 --type rust

# Find the FaucetInfo enum definition
rg -n 'enum FaucetInfo' -C5 --type rust

Length of output: 3174


🏁 Script executed:

#!/bin/bash
# Dump handle_erc20_claim function body
sed -n '312,360p' src/faucet/server_api.rs

# Dump FaucetInfo impl block to find methods
rg -n 'impl FaucetInfo' -C5 --type rust

# Search for drip_amount and token_type methods
rg -n 'fn drip_amount' -C3 --type rust
rg -n 'fn token_type' -C3 --type rust

Length of output: 3440


Check native FIL balance before ERC-20 transfers and mitigate TOCTOU race

  • For CalibnetUSDFC, after verifying the ERC-20 token balance, also call
    wallet_balance(from, &TokenType::Native) and ensure it covers the estimated gas cost.
  • The pre-transfer balance check runs before rate-limit enforcement, allowing concurrent requests to bypass limits and fail on-chain; consider advisory locking or moving rate-limit logic before this check.
🤖 Prompt for AI Agents
In src/faucet/server_api.rs around lines 222 to 230, after checking the ERC-20
faucet_balance add an additional check that calls rpc.wallet_balance(from,
&TokenType::Native).await and verify the returned native FIL balance is >= the
estimated gas cost for the transfer (compute or fetch the gas estimate used for
the transfer); if not, return a ServerFnError indicating insufficient native
funds to cover gas. To mitigate the TOCTOU/rate-limit race, move the rate-limit
enforcement (or acquire an advisory per-account lock) to run before any balance
checks so concurrent requests are serialized or rejected before on-chain state
is queried.

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