wallet-core: use division for withdrawal coin selection

This commit is contained in:
Florian Dold 2023-05-24 12:54:07 +02:00
parent da87ce41a6
commit ea953f2b77
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
3 changed files with 41 additions and 8 deletions

View File

@ -138,3 +138,13 @@ test("amount multiplication", (t) => {
t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 4).amount), "EUR:4.44");
t.is(Amounts.stringify(Amounts.mult(sAmt("EUR:1.11"), 5).amount), "EUR:5.55");
});
test("amount division", (t) => {
t.is(Amounts.divmod("EUR:5", "EUR:1").quotient, 5);
t.is(Amounts.stringify(Amounts.divmod("EUR:5", "EUR:1").remainder), "EUR:0");
t.is(Amounts.divmod("EUR:5", "EUR:2").quotient, 2);
t.is(Amounts.stringify(Amounts.divmod("EUR:5", "EUR:2").remainder), "EUR:1");
});

View File

@ -95,6 +95,11 @@ export interface Result {
*/
export type AmountLike = AmountString | AmountJson;
export interface DivmodResult {
quotient: number;
remainder: AmountJson;
}
/**
* Helper class for dealing with amounts.
*/
@ -135,6 +140,29 @@ export class Amounts {
return amt;
}
static divmod(a1: AmountLike, a2: AmountLike): DivmodResult {
const am1 = Amounts.jsonifyAmount(a1);
const am2 = Amounts.jsonifyAmount(a2);
if (am1.currency != am2.currency) {
throw Error(`incompatible currency (${am1.currency} vs${am2.currency})`);
}
const x1 = BigInt(am1.value) * BigInt(amountFractionalBase) + BigInt(am1.fraction);
const x2 = BigInt(am2.value) * BigInt(amountFractionalBase) + BigInt(am2.fraction);
const quotient = x1 / x2;
const remainderScaled = x1 % x2;
return {
quotient: Number(quotient),
remainder: {
currency: am1.currency,
value: Number(remainderScaled / BigInt(amountFractionalBase)),
fraction: Number(remainderScaled % BigInt(amountFractionalBase))
}
}
}
static sum(amounts: AmountLike[]): Result {
if (amounts.length <= 0) {
throw Error("can't sum zero amounts");

View File

@ -689,18 +689,13 @@ export function selectWithdrawalDenominations(
);
for (const d of denoms) {
let count = 0;
const cost = Amounts.add(
DenominationRecord.getValue(d),
d.fees.feeWithdraw,
).amount;
for (;;) {
if (Amounts.cmp(remaining, cost) < 0) {
break;
}
remaining = Amounts.sub(remaining, cost).amount;
count++;
}
const res = Amounts.divmod(remaining, cost);
const count = res.quotient;
remaining = Amounts.sub(remaining, Amounts.mult(cost, count).amount).amount;
if (count > 0) {
totalCoinValue = Amounts.add(
totalCoinValue,