fix(send): stop a fee-tier change from re-sending Max; compare affordability in satoshis
Fund-correctness audit, send path: - Flagship wrong-send: s_send_max was only cleared on a successful send, so after Max → typing a smaller amount → changing the fee tier in the confirm dialog, the amount was silently overwritten back to the maximum and Confirm sent far more than the user typed. Clear s_send_max on any manual amount edit (DRGX and USD inputs), so a later fee bump never rewrites a hand-entered amount. - A valid exact-maximum send was blocked by 1 ulp because amount+fee<=available was computed in IEEE doubles. New util::toSats/canAfford compare in int64 satoshis; wired into the Review gate, the confirm-time re-check, the Send tooltip and the amount-error banner. This only unblocks a correct send; it changes no sent amount. - Added util::coinsToZatoshis (reject non-finite / <=0 / rounds-to-0 / over-supply) and regression tests for formatAmountFixed, toSats/canAfford and coinsToZatoshis (incl. the exact failing inputs the audit found). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,11 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
namespace dragonx {
|
||||
namespace util {
|
||||
|
||||
std::string formatAmountFixed(double amount, int decimals = 8);
|
||||
|
||||
// DRGX amounts are 8-decimal. Converting to int64 satoshis and comparing there avoids the IEEE-double
|
||||
// rounding that could block a valid exact-max send (amount+fee computed in doubles landed 1 ulp over).
|
||||
inline std::int64_t toSats(double coins) { return static_cast<std::int64_t>(std::llround(coins * 1e8)); }
|
||||
|
||||
// True when amount + fee fits within available, compared in integer satoshis (not doubles).
|
||||
inline bool canAfford(double amount, double fee, double available) {
|
||||
return toSats(amount) + toSats(fee) <= toSats(available);
|
||||
}
|
||||
|
||||
// Convert a coin amount to zatoshis for a send, rejecting non-finite / non-positive / rounds-to-zero /
|
||||
// over-supply values. Returns false (and leaves out untouched) on any of those.
|
||||
inline bool coinsToZatoshis(double coins, std::uint64_t& out) {
|
||||
if (!std::isfinite(coins) || coins <= 0.0) return false;
|
||||
const std::int64_t z = toSats(coins);
|
||||
if (z <= 0 || z > static_cast<std::int64_t>(21000000) * 100000000LL) return false;
|
||||
out = static_cast<std::uint64_t>(z);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace dragonx
|
||||
|
||||
Reference in New Issue
Block a user