From 33266b3c144c68e9a8e601842db99c61a31d74f5 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 30 May 2026 18:08:27 +0000 Subject: [PATCH] Add optional unsweep amount --- README.md | 2 +- src/salvium_tipbot_commands.php | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index efae697..a544e41 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ A PHP-based Telegram tip bot for the Salvium (Monero fork) cryptocurrency. ## Commands - `/withdraw
` withdraws `SAL1` - `/withdraw
` withdraws `SAL1` or a token asset, for example `CULT`/`salCULT` -- `/unsweep ` admin-only command that self-transfers unlocked wallet funds into multiple smaller outputs +- `/unsweep [amount]` admin-only command that self-transfers unlocked wallet funds into multiple smaller outputs ## Withdrawal fees - `WITHDRAWAL_FEE` is charged in `SAL1` for `SAL1` withdrawals. diff --git a/src/salvium_tipbot_commands.php b/src/salvium_tipbot_commands.php index 519b7d6..abf4bf8 100644 --- a/src/salvium_tipbot_commands.php +++ b/src/salvium_tipbot_commands.php @@ -266,9 +266,9 @@ class SalviumTipBotCommands { } try { - [$assetType, $outputCount] = $this->parseUnsweepArgs($args); + [$assetType, $outputCount, $requestedAmountAtomic] = $this->parseUnsweepArgs($args); } catch (Throwable $e) { - return "Usage: /unsweep "; + return "Usage: /unsweep [amount]"; } $maxOutputs = max(2, (int)($this->config['MAX_UNSWEEP_OUTPUTS'] ?? 100)); @@ -315,7 +315,11 @@ class SalviumTipBotCommands { $reserveAtomic = $this->humanToAtomic($this->config['UNSWEEP_SAL1_FEE_RESERVE'] ?? 0.5); } - $spendableAtomic = $unlockedAtomic - $reserveAtomic; + $spendableAtomic = $requestedAmountAtomic ?? ($unlockedAtomic - $reserveAtomic); + if ($requestedAmountAtomic !== null && $spendableAtomic > ($unlockedAtomic - $reserveAtomic)) { + return "Requested amount exceeds spendable unlocked {$assetType}. Max is " . $this->atomicToHuman(max(0, $unlockedAtomic - $reserveAtomic)) . " {$assetType}."; + } + if ($spendableAtomic <= 0) { return "No spendable {$assetType} after fee reserve of " . $this->atomicToHuman($reserveAtomic) . " {$assetType}."; } @@ -346,8 +350,9 @@ class SalviumTipBotCommands { $totalAtomic = $amountPerOutputAtomic * $outputCount; $reserveText = $reserveAtomic > 0 ? " Reserved " . $this->atomicToHuman($reserveAtomic) . " {$assetType} for fee/change." : ""; + $sourceText = $requestedAmountAtomic !== null ? " Requested " . $this->atomicToHuman($requestedAmountAtomic) . " {$assetType}." : ""; return "Unswept " . $this->atomicToHuman($totalAtomic) . " {$assetType} into {$outputCount} outputs of " - . $this->atomicToHuman($amountPerOutputAtomic) . " {$assetType}.{$reserveText} " + . $this->atomicToHuman($amountPerOutputAtomic) . " {$assetType}.{$sourceText}{$reserveText} " . $this->formatTxids($txids); } @@ -492,7 +497,7 @@ class SalviumTipBotCommands { } private function parseUnsweepArgs(array $args): array { - if (count($args) !== 3 || !$this->looksLikeAssetType($args[1]) || !ctype_digit((string)$args[2])) { + if (!in_array(count($args), [3, 4], true) || !$this->looksLikeAssetType($args[1]) || !ctype_digit((string)$args[2])) { throw new InvalidArgumentException("Invalid /unsweep arguments"); } @@ -501,7 +506,18 @@ class SalviumTipBotCommands { throw new InvalidArgumentException("Invalid /unsweep output count"); } - return [$this->parseAssetType($args[1]), $outputCount]; + $requestedAmountAtomic = null; + if (count($args) === 4) { + if (!is_numeric($args[3]) || (float)$args[3] <= 0) { + throw new InvalidArgumentException("Invalid /unsweep amount"); + } + $requestedAmountAtomic = $this->humanToAtomic($args[3]); + if ($requestedAmountAtomic <= 0) { + throw new InvalidArgumentException("Invalid /unsweep amount"); + } + } + + return [$this->parseAssetType($args[1]), $outputCount, $requestedAmountAtomic]; } private function atomicSubtractNonNegative(string $left, string $right): string {