aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-util/src
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-11-02 17:42:14 +0100
committerFlorian Dold <florian@dold.me>2022-11-02 18:23:17 +0100
commitd50294f76e0aa357d690a933bb6d696a2f6aef1b (patch)
treeabe961337c5df1614b5095bce6c5b09e761cda2c /packages/taler-util/src
parent6c3ef31d9a7ba44829e779afed0af9be3ab23723 (diff)
downloadwallet-core-d50294f76e0aa357d690a933bb6d696a2f6aef1b.tar.xz
wallet-core: DB FIXMEs (amount format)
Diffstat (limited to 'packages/taler-util/src')
-rw-r--r--packages/taler-util/src/amounts.ts78
-rw-r--r--packages/taler-util/src/wallet-types.ts52
2 files changed, 83 insertions, 47 deletions
diff --git a/packages/taler-util/src/amounts.ts b/packages/taler-util/src/amounts.ts
index c9a78356e..f59e325b0 100644
--- a/packages/taler-util/src/amounts.ts
+++ b/packages/taler-util/src/amounts.ts
@@ -103,10 +103,24 @@ export class Amounts {
throw Error("not instantiable");
}
+ static currencyOf(amount: AmountLike) {
+ const amt = Amounts.parseOrThrow(amount);
+ return amt.currency;
+ }
+
+ static zeroOfAmount(amount: AmountLike): AmountJson {
+ const amt = Amounts.parseOrThrow(amount);
+ return {
+ currency: amt.currency,
+ fraction: 0,
+ value: 0,
+ };
+ }
+
/**
* Get an amount that represents zero units of a currency.
*/
- static getZero(currency: string): AmountJson {
+ static zeroOfCurrency(currency: string): AmountJson {
return {
currency,
fraction: 0,
@@ -132,7 +146,7 @@ export class Amounts {
static sumOrZero(currency: string, amounts: AmountLike[]): Result {
if (amounts.length <= 0) {
return {
- amount: Amounts.getZero(currency),
+ amount: Amounts.zeroOfCurrency(currency),
saturated: false,
};
}
@@ -147,9 +161,11 @@ export class Amounts {
*
* Throws when currencies don't match.
*/
- static add(first: AmountJson, ...rest: AmountJson[]): Result {
- const currency = first.currency;
- let value = first.value + Math.floor(first.fraction / amountFractionalBase);
+ static add(first: AmountLike, ...rest: AmountLike[]): Result {
+ const firstJ = Amounts.jsonifyAmount(first);
+ const currency = firstJ.currency;
+ let value =
+ firstJ.value + Math.floor(firstJ.fraction / amountFractionalBase);
if (value > amountMaxValue) {
return {
amount: {
@@ -160,17 +176,18 @@ export class Amounts {
saturated: true,
};
}
- let fraction = first.fraction % amountFractionalBase;
+ let fraction = firstJ.fraction % amountFractionalBase;
for (const x of rest) {
- if (x.currency.toUpperCase() !== currency.toUpperCase()) {
- throw Error(`Mismatched currency: ${x.currency} and ${currency}`);
+ const xJ = Amounts.jsonifyAmount(x);
+ if (xJ.currency.toUpperCase() !== currency.toUpperCase()) {
+ throw Error(`Mismatched currency: ${xJ.currency} and ${currency}`);
}
value =
value +
- x.value +
- Math.floor((fraction + x.fraction) / amountFractionalBase);
- fraction = Math.floor((fraction + x.fraction) % amountFractionalBase);
+ xJ.value +
+ Math.floor((fraction + xJ.fraction) / amountFractionalBase);
+ fraction = Math.floor((fraction + xJ.fraction) % amountFractionalBase);
if (value > amountMaxValue) {
return {
amount: {
@@ -322,12 +339,27 @@ export class Amounts {
* Parse amount in standard string form (like 'EUR:20.5'),
* throw if the input is not a valid amount.
*/
- static parseOrThrow(s: string): AmountJson {
- const res = Amounts.parse(s);
- if (!res) {
- throw Error(`Can't parse amount: "${s}"`);
+ static parseOrThrow(s: AmountLike): AmountJson {
+ if (typeof s === "object") {
+ if (typeof s.currency !== "string") {
+ throw Error("invalid amount object");
+ }
+ if (typeof s.value !== "number") {
+ throw Error("invalid amount object");
+ }
+ if (typeof s.fraction !== "number") {
+ throw Error("invalid amount object");
+ }
+ return { currency: s.currency, value: s.value, fraction: s.fraction };
+ } else if (typeof s === "string") {
+ const res = Amounts.parse(s);
+ if (!res) {
+ throw Error(`Can't parse amount: "${s}"`);
+ }
+ return res;
+ } else {
+ throw Error("invalid amount (illegal type)");
}
- return res;
}
/**
@@ -371,10 +403,13 @@ export class Amounts {
throw Error("amount can only be multiplied by a positive integer");
}
if (n == 0) {
- return { amount: Amounts.getZero(a.currency), saturated: false };
+ return {
+ amount: Amounts.zeroOfCurrency(a.currency),
+ saturated: false,
+ };
}
let x = a;
- let acc = Amounts.getZero(a.currency);
+ let acc = Amounts.zeroOfCurrency(a.currency);
while (n > 1) {
if (n % 2 == 0) {
n = n / 2;
@@ -427,9 +462,10 @@ export class Amounts {
return x1.currency.toUpperCase() === x2.currency.toUpperCase();
}
- static stringifyValue(a: AmountJson, minFractional = 0): string {
- const av = a.value + Math.floor(a.fraction / amountFractionalBase);
- const af = a.fraction % amountFractionalBase;
+ static stringifyValue(a: AmountLike, minFractional = 0): string {
+ const aJ = Amounts.jsonifyAmount(a);
+ const av = aJ.value + Math.floor(aJ.fraction / amountFractionalBase);
+ const af = aJ.fraction % amountFractionalBase;
let s = av.toString();
if (af) {
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 5ff906faa..5d1c55b88 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -644,7 +644,7 @@ export enum RefreshReason {
*/
export interface CoinRefreshRequest {
readonly coinPub: string;
- readonly amount: AmountJson;
+ readonly amount: AmountString;
}
/**
@@ -719,12 +719,12 @@ export interface WireFee {
/**
* Fee for wire transfers.
*/
- wireFee: AmountJson;
+ wireFee: AmountString;
/**
* Fees to close and refund a reserve.
*/
- closingFee: AmountJson;
+ closingFee: AmountString;
/**
* Start date of the fee.
@@ -761,9 +761,9 @@ export interface ExchangeGlobalFees {
startDate: TalerProtocolTimestamp;
endDate: TalerProtocolTimestamp;
- historyFee: AmountJson;
- accountFee: AmountJson;
- purseFee: AmountJson;
+ historyFee: AmountString;
+ accountFee: AmountString;
+ purseFee: AmountString;
historyTimeout: TalerProtocolDuration;
purseTimeout: TalerProtocolDuration;
@@ -782,8 +782,8 @@ const codecForExchangeAccount = (): Codec<ExchangeAccount> =>
const codecForWireFee = (): Codec<WireFee> =>
buildCodecForObject<WireFee>()
.property("sig", codecForString())
- .property("wireFee", codecForAmountJson())
- .property("closingFee", codecForAmountJson())
+ .property("wireFee", codecForAmountString())
+ .property("closingFee", codecForAmountString())
.property("startStamp", codecForTimestamp)
.property("endStamp", codecForTimestamp)
.build("codecForWireFee");
@@ -798,7 +798,7 @@ export interface DenominationInfo {
/**
* Value of one coin of the denomination.
*/
- value: AmountJson;
+ value: AmountString;
/**
* Hash of the denomination public key.
@@ -811,22 +811,22 @@ export interface DenominationInfo {
/**
* Fee for withdrawing.
*/
- feeWithdraw: AmountJson;
+ feeWithdraw: AmountString;
/**
* Fee for depositing.
*/
- feeDeposit: AmountJson;
+ feeDeposit: AmountString;
/**
* Fee for refreshing.
*/
- feeRefresh: AmountJson;
+ feeRefresh: AmountString;
/**
* Fee for refunding.
*/
- feeRefund: AmountJson;
+ feeRefund: AmountString;
/**
* Validity start date of the denomination.
@@ -858,21 +858,21 @@ export interface FeeDescription {
group: string;
from: AbsoluteTime;
until: AbsoluteTime;
- fee?: AmountJson;
+ fee?: AmountString;
}
export interface FeeDescriptionPair {
group: string;
from: AbsoluteTime;
until: AbsoluteTime;
- left?: AmountJson;
- right?: AmountJson;
+ left?: AmountString;
+ right?: AmountString;
}
export interface TimePoint<T> {
id: string;
group: string;
- fee: AmountJson;
+ fee: AmountString;
type: "start" | "end";
moment: AbsoluteTime;
denom: T;
@@ -955,8 +955,8 @@ export const codecForFeeDescriptionPair = (): Codec<FeeDescriptionPair> =>
.property("group", codecForString())
.property("from", codecForAbsoluteTime)
.property("until", codecForAbsoluteTime)
- .property("left", codecOptional(codecForAmountJson()))
- .property("right", codecOptional(codecForAmountJson()))
+ .property("left", codecOptional(codecForAmountString()))
+ .property("right", codecOptional(codecForAmountString()))
.build("FeeDescriptionPair");
export const codecForFeeDescription = (): Codec<FeeDescription> =>
@@ -964,7 +964,7 @@ export const codecForFeeDescription = (): Codec<FeeDescription> =>
.property("group", codecForString())
.property("from", codecForAbsoluteTime)
.property("until", codecForAbsoluteTime)
- .property("fee", codecOptional(codecForAmountJson()))
+ .property("fee", codecOptional(codecForAmountString()))
.build("FeeDescription");
export const codecForFeesByOperations = (): Codec<
@@ -1056,8 +1056,8 @@ export interface ManualWithdrawalDetails {
* Selected denominations withn some extra info.
*/
export interface DenomSelectionState {
- totalCoinValue: AmountJson;
- totalWithdrawCost: AmountJson;
+ totalCoinValue: AmountString;
+ totalWithdrawCost: AmountString;
selectedDenoms: {
denomPubHash: string;
count: number;
@@ -1786,7 +1786,7 @@ export interface PayCoinSelection {
/**
* Amount requested by the merchant.
*/
- paymentAmount: AmountJson;
+ paymentAmount: AmountString;
/**
* Public keys of the coins that were selected.
@@ -1796,17 +1796,17 @@ export interface PayCoinSelection {
/**
* Amount that each coin contributes.
*/
- coinContributions: AmountJson[];
+ coinContributions: AmountString[];
/**
* How much of the wire fees is the customer paying?
*/
- customerWireFees: AmountJson;
+ customerWireFees: AmountString;
/**
* How much of the deposit fees is the customer paying?
*/
- customerDepositFees: AmountJson;
+ customerDepositFees: AmountString;
}
export interface InitiatePeerPushPaymentRequest {