Skip to content

feat: add recipient balance filtering to faucet#273

Open
ivegabr wants to merge 2 commits into
mainfrom
feat/DX-1913-max-balance-limit
Open

feat: add recipient balance filtering to faucet#273
ivegabr wants to merge 2 commits into
mainfrom
feat/DX-1913-max-balance-limit

Conversation

@ivegabr

@ivegabr ivegabr commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

description

There are wallets that are abusing the usage of the faucet by getting tRBTC every day.

As an example, address 0xC3071E6e19907f7238174BAFac20c5b5e34C81F1

https://explorer.testnet.rootstock.io/address/0xc3071e6e19907f7238174bafac20c5b5e34c81f1?tab=txs

has accumulated more than 5 trbtc.

In order to avoid that level of abuse, we will set up a maximum balance for the receiving address of 0.1 tRBTC.

summary of changes

  • Introduced new environment variables: FILTER_BY_BALANCE and MAX_RECEIVER_BALANCE to control recipient balance checks.

- Introduced new environment variables: `FILTER_BY_BALANCE` and `MAX_RECEIVER_BALANCE` to control recipient balance checks.
Copilot AI review requested due to automatic review settings June 9, 2026 13:48
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Copilot AI 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.

Pull request overview

Adds an optional “recipient balance cap” to reduce faucet abuse by rejecting dispenses to addresses whose on-chain balance exceeds a configurable threshold.

Changes:

  • Added FILTER_BY_BALANCE and MAX_RECEIVER_BALANCE configuration and documentation.
  • Implemented recipient balance validation (with promo-code bypass).
  • Updated deployment workflows to pass the new env vars.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/utils/validations.ts Adds receiverBalanceExceeded validation based on configured maximum balance.
src/utils/startup-context.ts Exposes balance-filter settings in startup context logging.
src/constants/env.ts Adds server env parsing for new balance-filter variables.
src/app/lib/action.ts Fetches recipient balance (when enabled) and runs new validation.
README.md Documents new env vars and dispense limit behaviors.
.github/workflows/prod-deploy.yml Passes new env vars into deployment environment generation.
.github/workflows/dev-deploy.yml Passes new env vars into deployment environment generation.
.env.example Adds new env vars to example configuration.

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

Comment thread src/app/lib/action.ts
Comment on lines +86 to +89
const recipientBalanceWei =
serverEnv.FILTER_BY_BALANCE && !promoCode
? BigInt(await web3.eth.getBalance(dispenseAddress))
: BigInt(0);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/utils/validations.ts
Comment on lines +35 to +40
const maxBalanceWei = BigInt(
Web3.utils.toWei(serverEnv.MAX_RECEIVER_BALANCE.toString(), 'ether')
);
if (recipientBalanceWei > maxBalanceWei) {
return `This address already holds more than ${serverEnv.MAX_RECEIVER_BALANCE} test RBTC and cannot receive more from the faucet.`;
}
Comment thread src/constants/env.ts
Comment on lines 25 to 28
FILTER_BY_IP: process.env.FILTER_BY_IP === 'true',
FILTER_BY_BALANCE: process.env.FILTER_BY_BALANCE === 'true',
MAX_RECEIVER_BALANCE: Number(process.env.MAX_RECEIVER_BALANCE ?? '0.1'),
TIMER_LIMIT: Number(process.env.TIMER_LIMIT),
Comment on lines 117 to 120
FILTER_BY_IP: ${{ secrets.FILTER_BY_IP }}
FILTER_BY_BALANCE: ${{ secrets.FILTER_BY_BALANCE }}
MAX_RECEIVER_BALANCE: ${{ secrets.MAX_RECEIVER_BALANCE }}
TIMER_LIMIT: ${{ secrets.TIMER_LIMIT }}
Comment on lines 124 to 127
FILTER_BY_IP: ${{ secrets.FILTER_BY_IP }}
FILTER_BY_BALANCE: ${{ secrets.FILTER_BY_BALANCE }}
MAX_RECEIVER_BALANCE: ${{ secrets.MAX_RECEIVER_BALANCE }}
TIMER_LIMIT: ${{ secrets.TIMER_LIMIT }}

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Comment thread src/constants/env.ts Outdated
Comment on lines +3 to +9
function parseMaxReceiverBalance(): number {
const raw = process.env.MAX_RECEIVER_BALANCE ?? String(DEFAULT_MAX_RECEIVER_BALANCE);
const value = Number(raw);
if (!Number.isFinite(value) || value < 0) {
return DEFAULT_MAX_RECEIVER_BALANCE;
}
return value;
@@ -1,13 +1,16 @@
import { ValidationError } from '@/utils/validations';
Comment on lines +31 to 33
filterByBalance: process.env.FILTER_BY_BALANCE === 'true',
maxReceiverBalance: Number(process.env.MAX_RECEIVER_BALANCE ?? '0.1') || undefined,
timerLimitMs: Number(process.env.TIMER_LIMIT) || undefined,
Comment thread src/app/lib/action.ts Outdated
Comment on lines +264 to +267
if (shouldCheckBalance) {
const recipientBalanceWei = BigInt(await web3.eth.getBalance(dispenseAddress));
addOutcome(receiverBalanceExceeded(recipientBalanceWei, promoCode));
}
Comment thread README.md Outdated
Comment on lines +64 to +66
| Per IP | `FILTER_BY_IP` | `false` | Rejects if the same IP already requested within `TIMER_LIMIT` |
| Per address | `TIMER_LIMIT` | `180000` ms (3 min) | Rejects if the recipient address already received tokens within the timer window |
| Recipient balance | `FILTER_BY_BALANCE`, `MAX_RECEIVER_BALANCE` | disabled, `0.1` RBTC | When enabled, rejects if the recipient already holds `MAX_RECEIVER_BALANCE` tRBTC or more |
Comment thread README.md
| Faucet balance | — | — | Rejects if the faucet wallet has less than 0.1 tRBTC |
| Captcha | — | — | Rejects if reCAPTCHA verification fails |

`TIMER_LIMIT` applies to the per-address check. The faucet history is also reset daily at midnight (Pacific time).
Comment thread README.md
|---|---|
| Per IP (`FILTER_BY_IP`) | Yes |
| Recipient balance (`FILTER_BY_BALANCE`) | Yes |
| Per address (`TIMER_LIMIT`) | No — the same address still cannot receive again within the timer window |
@ivegabr ivegabr force-pushed the feat/DX-1913-max-balance-limit branch from e50c877 to 41b7013 Compare June 9, 2026 18:50
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