Skip to content

feat: enforce min/max sats amount on orders#778

Open
ToRyVand wants to merge 3 commits intolnp2pBot:mainfrom
ToRyVand:feat/issue-406-min-max-sats
Open

feat: enforce min/max sats amount on orders#778
ToRyVand wants to merge 3 commits intolnp2pBot:mainfrom
ToRyVand:feat/issue-406-min-max-sats

Conversation

@ToRyVand
Copy link
Copy Markdown

@ToRyVand ToRyVand commented Apr 9, 2026

Summary

Adds configurable MAX_PAYMENT_AMT environment variable alongside the existing MIN_PAYMENT_AMT to enforce sats amount limits on all orders.

Closes #406

Changes

File What
.env-sample Added MAX_PAYMENT_AMT=10000000
bot/validations.ts Max check in validateSellOrder and validateBuyOrder
bot/messages.ts New mustBeLessEqThan message helper
bot/modules/orders/scenes.ts Min/max validation in the interactive wizard
locales/*.yaml (10 files) Added must_be_lt_or_eq key in all languages
tests/bot/validation.spec.ts 9 new tests for boundary and edge cases

Behavior

  • Backwards compatible: if MAX_PAYMENT_AMT is not set, no upper limit is enforced
  • Market price orders (amount=0) always bypass both min/max checks
  • Boundary: amount == MAX_PAYMENT_AMT is accepted, amount > MAX_PAYMENT_AMT is rejected
  • Wizard fix: the interactive order wizard now also validates MIN_PAYMENT_AMT (previously only the command-line path did)

Tests

141/141 passing — includes new tests:

  • Amount exceeds maximum → rejected
  • Amount equals maximum → accepted
  • Amount one above maximum → rejected
  • Market price (amount=0) with max set → accepted
  • No MAX_PAYMENT_AMT configured → no limit (retrocompatible)
npm run lint   ✅
npm run format ✅  
npm test       ✅ 141 passing

Summary by CodeRabbit

  • New Features

    • Order processing now enforces a configurable maximum payment amount; orders above the limit are rejected.
    • Clear, localized validation messages for the "less or equal" constraint added across multiple languages.
  • Documentation

    • Sample environment file includes a MAX_PAYMENT_AMT default value.
  • Tests

    • New automated tests cover max-payment behavior, edge cases and zero-amount handling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

Warning

Rate limit exceeded

@ToRyVand has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 34 minutes and 27 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ab3b5f0e-bbc4-49d5-bf7d-46a944e553ec

📥 Commits

Reviewing files that changed from the base of the PR and between c57f157 and c241039.

📒 Files selected for processing (2)
  • .env-sample
  • bot/validations.ts

Walkthrough

Adds a MAX_PAYMENT_AMT env var and enforces an upper payment bound in order handlers and validations; introduces a "less or equal" message helper and translations; updates tests to cover max/min payment behavior.

Changes

Payment upper-bound and validation flow

Layer / File(s) Summary
Configuration / Default
.env-sample
Added MAX_PAYMENT_AMT=10000000 (maximum payment amount in satoshis).
Message Helper
bot/messages.ts
Added exported async helper mustBeLessEqThan(ctx, fieldName, qty) to send i18n must_be_lt_or_eq and log errors.
Handlers Export / Input Flow
bot/modules/orders/scenes.ts
Exported createOrderHandlers; createOrderHandlers.sats parses/floors numeric input, enforces MIN_PAYMENT_AMT/MAX_PAYMENT_AMT for non‑zero values, sets ctx.wizard.state.error with must_be_gt_or_eq/must_be_lt_or_eq, updates UI, and stores valid ctx.wizard.state.sats.
Validation Logic
bot/validations.ts
validateSellOrder and validateBuyOrder parse process.env.MAX_PAYMENT_AMT and reject when amount !== 0 && maxPaymentAmt > 0 && amount > maxPaymentAmt by calling messages.mustBeLessEqThan(...).
Localization
locales/*.yaml
Added must_be_lt_or_eq translation key in locales: de, en, es, fa, fr, it, ko, pt, ru, uk with ${fieldName}/${qty} templates.
Tests
tests/bot/validation.spec.ts
Added tests for validateSellOrder/validateBuyOrder and createOrderHandlers.sats covering: amount > MAX rejects, amount == MAX accepts, MAX unset skips check, amount == 0 allowed, decimal input floored (including when it equals max).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • mostronatorcoder
  • Catrya
  • grunch

Poem

🐰 I hopped through code with careful cheer,
A ceiling set for sats so dear,
Ten million marks the line we trace,
Translations speak in every place,
Hooray — small bounds, a tidy space!

🚥 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 'feat: enforce min/max sats amount on orders' accurately describes the main objective of the PR, which adds enforcement of both minimum and maximum satoshi payment amounts.
Linked Issues check ✅ Passed The PR addresses issue #406 by implementing configurable MIN_PAYMENT_AMT and MAX_PAYMENT_AMT validation across order validation functions, wizard interactive input, and multiple locale translations.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing min/max satoshi amount enforcement. The export of createOrderHandlers is necessary for test coverage and doesn't introduce unrelated functionality.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

Copy link
Copy Markdown
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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bot/modules/orders/scenes.ts (1)

307-339: ⚠️ Potential issue | 🟡 Minor

Validate limits against the normalized sats value.

At Line [329], max validation compares the raw numeric input, but at Line [337] the stored amount is Math.floor(input). This can reject inputs that would persist as valid integer sats, diverging from command-path behavior.

💡 Suggested patch
-    const input = Number(ctx.message?.text);
+    const rawInput = Number(ctx.message?.text);
     await ctx.deleteMessage();
-    if (isNaN(input)) {
+    if (isNaN(rawInput)) {
       ctx.wizard.state.error = ctx.i18n.t('not_number');
       await ctx.wizard.state.updateUI();
       return;
     }
-    if (input < 0) {
+    if (rawInput < 0) {
       ctx.wizard.state.error = ctx.i18n.t('not_negative');
       await ctx.wizard.state.updateUI();
       return;
     }
+    const input = Math.floor(rawInput);
     const minPaymentAmt = Number(process.env.MIN_PAYMENT_AMT) || 0;
     const maxPaymentAmt = Number(process.env.MAX_PAYMENT_AMT) || 0;
@@
-    ctx.wizard.state.sats = Math.floor(input);
+    ctx.wizard.state.sats = input;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bot/modules/orders/scenes.ts` around lines 307 - 339, The validation uses the
raw Number(input) for min/max checks but later stores Math.floor(input) into
ctx.wizard.state.sats, causing mismatches; normalize the input once (e.g., const
normalized = Math.floor(input)) immediately after the NaN/negative checks and
use that normalized value for the minPaymentAmt/maxPaymentAmt comparisons and
for assigning ctx.wizard.state.sats, while preserving the existing guards that
allow 0 as a special case.
🧹 Nitpick comments (2)
.env-sample (1)

54-55: Consider clarifying optional behavior in the sample comment.
To avoid confusion, add a note that leaving MAX_PAYMENT_AMT unset disables the upper bound (as intended for backward compatibility).

Based on learnings: document new environment variables in .env-sample and keep behavior clear for .env users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env-sample around lines 54 - 55, Update the comment above the
MAX_PAYMENT_AMT env var to explicitly state that this setting is optional and
that leaving MAX_PAYMENT_AMT unset disables the upper bound (preserving backward
compatibility); mention the units (satoshis) and the default behavior so users
know the sample value (10000000) is only an example and not required.
tests/bot/validation.spec.ts (1)

146-209: Good boundary coverage for command validations; add wizard-path parity tests too.

These additions cover command-based max checks well. Since wizard flow has separate min/max logic, add corresponding tests for createOrderHandlers.sats to prevent command/wizard drift on edge inputs.

Also applies to: 285-342

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/bot/validation.spec.ts` around lines 146 - 209, Add parallel tests that
exercise the wizard flow handler createOrderHandlers.sats mirroring the
command-path cases you added for validateSellOrder: stub process.env for
MIN_PAYMENT_AMT and MAX_PAYMENT_AMT and test scenarios for amount > MAX (expect
failure and reply), amount == MAX (expect success/object), MAX unset (skip max
check and succeed), amount 0 with range (market price allowed and result.amount
=== 0), and amount == MAX+1 (expect failure). Reuse the same sandbox setup and
assertion style (replyStub checks, expect(...).to.equal(false) or
.to.be.an('object')) so wizard-path logic stays in parity with
validateSellOrder.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bot/validations.ts`:
- Around line 215-226: Normalize and cache MAX_PAYMENT_AMT as a numeric value
before the max-amount guard in both validateSellOrder and validateBuyOrder:
parse process.env.MAX_PAYMENT_AMT into a const (e.g., maxPaymentAmt =
Number(process.env.MAX_PAYMENT_AMT)), treat non-finite or zero values as
“disabled” (skip the cap), and then only enforce the check when maxPaymentAmt is
a finite > 0 by replacing the existing condition (amount !== 0 &&
process.env.MAX_PAYMENT_AMT && amount > Number(...)) with a condition that uses
maxPaymentAmt (e.g., amount !== 0 && Number.isFinite(maxPaymentAmt) &&
maxPaymentAmt > 0 && amount > maxPaymentAmt) and keep the same
messages.mustBeLessEqThan call using maxPaymentAmt.

---

Outside diff comments:
In `@bot/modules/orders/scenes.ts`:
- Around line 307-339: The validation uses the raw Number(input) for min/max
checks but later stores Math.floor(input) into ctx.wizard.state.sats, causing
mismatches; normalize the input once (e.g., const normalized =
Math.floor(input)) immediately after the NaN/negative checks and use that
normalized value for the minPaymentAmt/maxPaymentAmt comparisons and for
assigning ctx.wizard.state.sats, while preserving the existing guards that allow
0 as a special case.

---

Nitpick comments:
In @.env-sample:
- Around line 54-55: Update the comment above the MAX_PAYMENT_AMT env var to
explicitly state that this setting is optional and that leaving MAX_PAYMENT_AMT
unset disables the upper bound (preserving backward compatibility); mention the
units (satoshis) and the default behavior so users know the sample value
(10000000) is only an example and not required.

In `@tests/bot/validation.spec.ts`:
- Around line 146-209: Add parallel tests that exercise the wizard flow handler
createOrderHandlers.sats mirroring the command-path cases you added for
validateSellOrder: stub process.env for MIN_PAYMENT_AMT and MAX_PAYMENT_AMT and
test scenarios for amount > MAX (expect failure and reply), amount == MAX
(expect success/object), MAX unset (skip max check and succeed), amount 0 with
range (market price allowed and result.amount === 0), and amount == MAX+1
(expect failure). Reuse the same sandbox setup and assertion style (replyStub
checks, expect(...).to.equal(false) or .to.be.an('object')) so wizard-path logic
stays in parity with validateSellOrder.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4c284b48-2a37-4002-a663-ac1b39bf3f63

📥 Commits

Reviewing files that changed from the base of the PR and between 02145ef and 2129a12.

📒 Files selected for processing (15)
  • .env-sample
  • bot/messages.ts
  • bot/modules/orders/scenes.ts
  • bot/validations.ts
  • locales/de.yaml
  • locales/en.yaml
  • locales/es.yaml
  • locales/fa.yaml
  • locales/fr.yaml
  • locales/it.yaml
  • locales/ko.yaml
  • locales/pt.yaml
  • locales/ru.yaml
  • locales/uk.yaml
  • tests/bot/validation.spec.ts

Comment thread bot/validations.ts Outdated
Add configurable MAX_PAYMENT_AMT environment variable alongside the
existing MIN_PAYMENT_AMT to enforce sats amount limits on orders.

Changes:
- Add MAX_PAYMENT_AMT to .env-sample (default: 10000000)
- Add max validation to validateSellOrder and validateBuyOrder
- Add min/max validation to the interactive wizard (scenes.ts)
- Add mustBeLessEqThan message helper in messages.ts
- Add must_be_lt_or_eq locale key in all 10 languages
- Add tests for max exceeded, equal to max, boundary, and market
  price (amount=0) edge cases

The max check is optional: if MAX_PAYMENT_AMT is not set, no upper
limit is enforced (backwards compatible). Amount 0 (market price
orders) always bypasses both min and max checks.

Closes lnp2pBot#406
@ToRyVand ToRyVand force-pushed the feat/issue-406-min-max-sats branch from 2129a12 to 4afcbee Compare April 9, 2026 16:39
Ensure Math.floor is applied before comparing against MIN/MAX_PAYMENT_AMT
so the value checked matches the value stored in wizard state. Also export
createOrderHandlers and add wizard-path tests for min/max bounds.
@ToRyVand
Copy link
Copy Markdown
Author

ToRyVand commented May 5, 2026

Hey @mostronatorcoder @Catrya @grunch — pinging as suggested by CodeRabbit.

I've addressed the review feedback with a new commit:

Fix: normalize sats input before min/max validation

The wizard was comparing the raw decimal input against MAX_PAYMENT_AMT but storing Math.floor(input), causing inconsistency. For example, 5000.9 with MAX=5000 would be rejected even though it would have been stored as 5000 (a valid value). Now Math.floor is applied once before any comparison.

Also added 6 wizard-path tests for the min/max bounds (including the decimal normalization case), and exported createOrderHandlers to make it testable.

All 147 tests passing. Thanks for the review!

Replace the truthy-string check with Number.isFinite so NaN and Infinity
are explicitly handled. Also clarify in .env-sample that MAX_PAYMENT_AMT
is optional and leaving it unset disables the upper bound.
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.

Allow set a minimum and maximum amount on sats

1 participant