Tie the withdrawal minimum to the bet amount instead of a separate field
RoundConfig.min_amount_sats was an independently-configurable floor that could drift out of sync with bet_amount_sats for no real reason (deposits never had a server-side minimum anyway). Drop the field and enforce amount_sats >= config.bet_amount_sats directly in request_withdrawal. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,7 +27,6 @@ _CONFIG_FIELDS = (
|
||||
"bet_amount_sats",
|
||||
"round_duration_seconds",
|
||||
"round_cooldown_seconds",
|
||||
"min_amount_sats",
|
||||
"fee_rate_sat_vb",
|
||||
"rbf_timeout_seconds",
|
||||
"draw_animation_seconds",
|
||||
@@ -40,7 +39,6 @@ class RoundConfigResponse(BaseModel):
|
||||
bet_amount_sats: int
|
||||
round_duration_seconds: int
|
||||
round_cooldown_seconds: int
|
||||
min_amount_sats: int
|
||||
fee_rate_sat_vb: int
|
||||
rbf_timeout_seconds: int
|
||||
draw_animation_seconds: int
|
||||
@@ -52,7 +50,6 @@ class RoundConfigUpdate(BaseModel):
|
||||
bet_amount_sats: int | None = None
|
||||
round_duration_seconds: int | None = None
|
||||
round_cooldown_seconds: int | None = None
|
||||
min_amount_sats: int | None = None
|
||||
fee_rate_sat_vb: int | None = None
|
||||
rbf_timeout_seconds: int | None = None
|
||||
draw_animation_seconds: int | None = None
|
||||
|
||||
+1
-2
@@ -75,7 +75,7 @@ class RoundConfig(Base):
|
||||
"""Single-row operational config, DB-backed so it's editable without a redeploy.
|
||||
|
||||
Everything business/round-related lives here (round timing, bet amount, fee
|
||||
rate, RBF timeout, minimum amount) so an operator can tune it live. Secrets
|
||||
rate, RBF timeout) so an operator can tune it live. Secrets
|
||||
and infra wiring (master key, JWT secret, Electrum host, admin token,
|
||||
database URL) deliberately stay env-var-driven — those require a restart
|
||||
anyway and aren't safe to hot-swap."""
|
||||
@@ -92,7 +92,6 @@ class RoundConfig(Base):
|
||||
# NOT gate the actual draw, which still waits for a real confirmed block for
|
||||
# its entropy (rounds/scheduler.py) — that can take longer than this value.
|
||||
draw_animation_seconds: Mapped[int] = mapped_column(default=20)
|
||||
min_amount_sats: Mapped[int] = mapped_column(BigInteger, default=100_000_000)
|
||||
fee_rate_sat_vb: Mapped[int] = mapped_column(default=1)
|
||||
rbf_timeout_seconds: Mapped[int] = mapped_column(default=900)
|
||||
# Maintenance switch: when true, the round currently in progress still runs to
|
||||
|
||||
@@ -250,8 +250,6 @@
|
||||
<input id="admin-fee-address" class="mono" placeholder="plm1q...">
|
||||
<label for="admin-bet-amount">Bet amount (PLM)</label>
|
||||
<input id="admin-bet-amount" inputmode="decimal" placeholder="es. 10">
|
||||
<label for="admin-min-amount">Importo minimo deposito/prelievo (PLM)</label>
|
||||
<input id="admin-min-amount" inputmode="decimal" placeholder="es. 1">
|
||||
</div>
|
||||
<div>
|
||||
<label for="admin-round-duration">Durata round (secondi)</label>
|
||||
@@ -479,7 +477,6 @@ async function adminLoadConfig() {
|
||||
document.getElementById('admin-round-duration').value = data.round_duration_seconds;
|
||||
document.getElementById('admin-round-cooldown').value = data.round_cooldown_seconds;
|
||||
document.getElementById('admin-draw-animation').value = data.draw_animation_seconds;
|
||||
document.getElementById('admin-min-amount').value = data.min_amount_sats / SATS_PER_PLM;
|
||||
document.getElementById('admin-fee-rate').value = data.fee_rate_sat_vb;
|
||||
document.getElementById('admin-rbf-timeout').value = data.rbf_timeout_seconds;
|
||||
renderMaintenanceState(data.paused);
|
||||
@@ -533,14 +530,12 @@ async function adminSave() {
|
||||
const btn = document.getElementById('save-btn');
|
||||
const feeAddress = document.getElementById('admin-fee-address').value;
|
||||
const betAmountPlm = parseFloat(document.getElementById('admin-bet-amount').value);
|
||||
const minAmountPlm = parseFloat(document.getElementById('admin-min-amount').value);
|
||||
const body = {
|
||||
fee_address: feeAddress,
|
||||
bet_amount_sats: Math.round(betAmountPlm * SATS_PER_PLM),
|
||||
round_duration_seconds: parseInt(document.getElementById('admin-round-duration').value, 10),
|
||||
round_cooldown_seconds: parseInt(document.getElementById('admin-round-cooldown').value, 10),
|
||||
draw_animation_seconds: parseInt(document.getElementById('admin-draw-animation').value, 10),
|
||||
min_amount_sats: Math.round(minAmountPlm * SATS_PER_PLM),
|
||||
fee_rate_sat_vb: parseInt(document.getElementById('admin-fee-rate').value, 10),
|
||||
rbf_timeout_seconds: parseInt(document.getElementById('admin-rbf-timeout').value, 10),
|
||||
};
|
||||
|
||||
@@ -19,8 +19,8 @@ async def request_withdrawal(
|
||||
session: AsyncSession, client: ElectrumClient, user: User, external_address: str, amount_sats: int
|
||||
) -> Withdrawal:
|
||||
config = await get_round_config(session)
|
||||
if amount_sats < config.min_amount_sats:
|
||||
raise WithdrawalError(f"amount below the minimum of {config.min_amount_sats} sats")
|
||||
if amount_sats < config.bet_amount_sats:
|
||||
raise WithdrawalError(f"amount below the minimum of {config.bet_amount_sats} sats")
|
||||
|
||||
unspent = (
|
||||
await session.scalars(
|
||||
|
||||
Reference in New Issue
Block a user