wallet-core: DB FIXMEs (amount format)

This commit is contained in:
Florian Dold 2022-11-02 17:42:14 +01:00
parent 6c3ef31d9a
commit d50294f76e
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
28 changed files with 400 additions and 349 deletions

View File

@ -306,7 +306,7 @@ async function getProviderInfo(
status: "error", status: "error",
code: TalerErrorCode.ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED, code: TalerErrorCode.ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED,
hint: "provider did not have provider salt", hint: "provider did not have provider salt",
} };
} }
return { return {
status: "ok", status: "ok",
@ -1469,7 +1469,7 @@ async function updateUploadFees(
const addFee = (x: AmountLike) => { const addFee = (x: AmountLike) => {
x = Amounts.jsonifyAmount(x); x = Amounts.jsonifyAmount(x);
feePerCurrency[x.currency] = Amounts.add( feePerCurrency[x.currency] = Amounts.add(
feePerCurrency[x.currency] ?? Amounts.getZero(x.currency), feePerCurrency[x.currency] ?? Amounts.zeroOfAmount(x),
x, x,
).amount; ).amount;
}; };

View File

@ -103,10 +103,24 @@ export class Amounts {
throw Error("not instantiable"); 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. * Get an amount that represents zero units of a currency.
*/ */
static getZero(currency: string): AmountJson { static zeroOfCurrency(currency: string): AmountJson {
return { return {
currency, currency,
fraction: 0, fraction: 0,
@ -132,7 +146,7 @@ export class Amounts {
static sumOrZero(currency: string, amounts: AmountLike[]): Result { static sumOrZero(currency: string, amounts: AmountLike[]): Result {
if (amounts.length <= 0) { if (amounts.length <= 0) {
return { return {
amount: Amounts.getZero(currency), amount: Amounts.zeroOfCurrency(currency),
saturated: false, saturated: false,
}; };
} }
@ -147,9 +161,11 @@ export class Amounts {
* *
* Throws when currencies don't match. * Throws when currencies don't match.
*/ */
static add(first: AmountJson, ...rest: AmountJson[]): Result { static add(first: AmountLike, ...rest: AmountLike[]): Result {
const currency = first.currency; const firstJ = Amounts.jsonifyAmount(first);
let value = first.value + Math.floor(first.fraction / amountFractionalBase); const currency = firstJ.currency;
let value =
firstJ.value + Math.floor(firstJ.fraction / amountFractionalBase);
if (value > amountMaxValue) { if (value > amountMaxValue) {
return { return {
amount: { amount: {
@ -160,17 +176,18 @@ export class Amounts {
saturated: true, saturated: true,
}; };
} }
let fraction = first.fraction % amountFractionalBase; let fraction = firstJ.fraction % amountFractionalBase;
for (const x of rest) { for (const x of rest) {
if (x.currency.toUpperCase() !== currency.toUpperCase()) { const xJ = Amounts.jsonifyAmount(x);
throw Error(`Mismatched currency: ${x.currency} and ${currency}`); if (xJ.currency.toUpperCase() !== currency.toUpperCase()) {
throw Error(`Mismatched currency: ${xJ.currency} and ${currency}`);
} }
value = value =
value + value +
x.value + xJ.value +
Math.floor((fraction + x.fraction) / amountFractionalBase); Math.floor((fraction + xJ.fraction) / amountFractionalBase);
fraction = Math.floor((fraction + x.fraction) % amountFractionalBase); fraction = Math.floor((fraction + xJ.fraction) % amountFractionalBase);
if (value > amountMaxValue) { if (value > amountMaxValue) {
return { return {
amount: { amount: {
@ -322,12 +339,27 @@ export class Amounts {
* Parse amount in standard string form (like 'EUR:20.5'), * Parse amount in standard string form (like 'EUR:20.5'),
* throw if the input is not a valid amount. * throw if the input is not a valid amount.
*/ */
static parseOrThrow(s: string): AmountJson { 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); const res = Amounts.parse(s);
if (!res) { if (!res) {
throw Error(`Can't parse amount: "${s}"`); throw Error(`Can't parse amount: "${s}"`);
} }
return res; return res;
} else {
throw Error("invalid amount (illegal type)");
}
} }
/** /**
@ -371,10 +403,13 @@ export class Amounts {
throw Error("amount can only be multiplied by a positive integer"); throw Error("amount can only be multiplied by a positive integer");
} }
if (n == 0) { if (n == 0) {
return { amount: Amounts.getZero(a.currency), saturated: false }; return {
amount: Amounts.zeroOfCurrency(a.currency),
saturated: false,
};
} }
let x = a; let x = a;
let acc = Amounts.getZero(a.currency); let acc = Amounts.zeroOfCurrency(a.currency);
while (n > 1) { while (n > 1) {
if (n % 2 == 0) { if (n % 2 == 0) {
n = n / 2; n = n / 2;
@ -427,9 +462,10 @@ export class Amounts {
return x1.currency.toUpperCase() === x2.currency.toUpperCase(); return x1.currency.toUpperCase() === x2.currency.toUpperCase();
} }
static stringifyValue(a: AmountJson, minFractional = 0): string { static stringifyValue(a: AmountLike, minFractional = 0): string {
const av = a.value + Math.floor(a.fraction / amountFractionalBase); const aJ = Amounts.jsonifyAmount(a);
const af = a.fraction % amountFractionalBase; const av = aJ.value + Math.floor(aJ.fraction / amountFractionalBase);
const af = aJ.fraction % amountFractionalBase;
let s = av.toString(); let s = av.toString();
if (af) { if (af) {

View File

@ -644,7 +644,7 @@ export enum RefreshReason {
*/ */
export interface CoinRefreshRequest { export interface CoinRefreshRequest {
readonly coinPub: string; readonly coinPub: string;
readonly amount: AmountJson; readonly amount: AmountString;
} }
/** /**
@ -719,12 +719,12 @@ export interface WireFee {
/** /**
* Fee for wire transfers. * Fee for wire transfers.
*/ */
wireFee: AmountJson; wireFee: AmountString;
/** /**
* Fees to close and refund a reserve. * Fees to close and refund a reserve.
*/ */
closingFee: AmountJson; closingFee: AmountString;
/** /**
* Start date of the fee. * Start date of the fee.
@ -761,9 +761,9 @@ export interface ExchangeGlobalFees {
startDate: TalerProtocolTimestamp; startDate: TalerProtocolTimestamp;
endDate: TalerProtocolTimestamp; endDate: TalerProtocolTimestamp;
historyFee: AmountJson; historyFee: AmountString;
accountFee: AmountJson; accountFee: AmountString;
purseFee: AmountJson; purseFee: AmountString;
historyTimeout: TalerProtocolDuration; historyTimeout: TalerProtocolDuration;
purseTimeout: TalerProtocolDuration; purseTimeout: TalerProtocolDuration;
@ -782,8 +782,8 @@ const codecForExchangeAccount = (): Codec<ExchangeAccount> =>
const codecForWireFee = (): Codec<WireFee> => const codecForWireFee = (): Codec<WireFee> =>
buildCodecForObject<WireFee>() buildCodecForObject<WireFee>()
.property("sig", codecForString()) .property("sig", codecForString())
.property("wireFee", codecForAmountJson()) .property("wireFee", codecForAmountString())
.property("closingFee", codecForAmountJson()) .property("closingFee", codecForAmountString())
.property("startStamp", codecForTimestamp) .property("startStamp", codecForTimestamp)
.property("endStamp", codecForTimestamp) .property("endStamp", codecForTimestamp)
.build("codecForWireFee"); .build("codecForWireFee");
@ -798,7 +798,7 @@ export interface DenominationInfo {
/** /**
* Value of one coin of the denomination. * Value of one coin of the denomination.
*/ */
value: AmountJson; value: AmountString;
/** /**
* Hash of the denomination public key. * Hash of the denomination public key.
@ -811,22 +811,22 @@ export interface DenominationInfo {
/** /**
* Fee for withdrawing. * Fee for withdrawing.
*/ */
feeWithdraw: AmountJson; feeWithdraw: AmountString;
/** /**
* Fee for depositing. * Fee for depositing.
*/ */
feeDeposit: AmountJson; feeDeposit: AmountString;
/** /**
* Fee for refreshing. * Fee for refreshing.
*/ */
feeRefresh: AmountJson; feeRefresh: AmountString;
/** /**
* Fee for refunding. * Fee for refunding.
*/ */
feeRefund: AmountJson; feeRefund: AmountString;
/** /**
* Validity start date of the denomination. * Validity start date of the denomination.
@ -858,21 +858,21 @@ export interface FeeDescription {
group: string; group: string;
from: AbsoluteTime; from: AbsoluteTime;
until: AbsoluteTime; until: AbsoluteTime;
fee?: AmountJson; fee?: AmountString;
} }
export interface FeeDescriptionPair { export interface FeeDescriptionPair {
group: string; group: string;
from: AbsoluteTime; from: AbsoluteTime;
until: AbsoluteTime; until: AbsoluteTime;
left?: AmountJson; left?: AmountString;
right?: AmountJson; right?: AmountString;
} }
export interface TimePoint<T> { export interface TimePoint<T> {
id: string; id: string;
group: string; group: string;
fee: AmountJson; fee: AmountString;
type: "start" | "end"; type: "start" | "end";
moment: AbsoluteTime; moment: AbsoluteTime;
denom: T; denom: T;
@ -955,8 +955,8 @@ export const codecForFeeDescriptionPair = (): Codec<FeeDescriptionPair> =>
.property("group", codecForString()) .property("group", codecForString())
.property("from", codecForAbsoluteTime) .property("from", codecForAbsoluteTime)
.property("until", codecForAbsoluteTime) .property("until", codecForAbsoluteTime)
.property("left", codecOptional(codecForAmountJson())) .property("left", codecOptional(codecForAmountString()))
.property("right", codecOptional(codecForAmountJson())) .property("right", codecOptional(codecForAmountString()))
.build("FeeDescriptionPair"); .build("FeeDescriptionPair");
export const codecForFeeDescription = (): Codec<FeeDescription> => export const codecForFeeDescription = (): Codec<FeeDescription> =>
@ -964,7 +964,7 @@ export const codecForFeeDescription = (): Codec<FeeDescription> =>
.property("group", codecForString()) .property("group", codecForString())
.property("from", codecForAbsoluteTime) .property("from", codecForAbsoluteTime)
.property("until", codecForAbsoluteTime) .property("until", codecForAbsoluteTime)
.property("fee", codecOptional(codecForAmountJson())) .property("fee", codecOptional(codecForAmountString()))
.build("FeeDescription"); .build("FeeDescription");
export const codecForFeesByOperations = (): Codec< export const codecForFeesByOperations = (): Codec<
@ -1056,8 +1056,8 @@ export interface ManualWithdrawalDetails {
* Selected denominations withn some extra info. * Selected denominations withn some extra info.
*/ */
export interface DenomSelectionState { export interface DenomSelectionState {
totalCoinValue: AmountJson; totalCoinValue: AmountString;
totalWithdrawCost: AmountJson; totalWithdrawCost: AmountString;
selectedDenoms: { selectedDenoms: {
denomPubHash: string; denomPubHash: string;
count: number; count: number;
@ -1786,7 +1786,7 @@ export interface PayCoinSelection {
/** /**
* Amount requested by the merchant. * Amount requested by the merchant.
*/ */
paymentAmount: AmountJson; paymentAmount: AmountString;
/** /**
* Public keys of the coins that were selected. * Public keys of the coins that were selected.
@ -1796,17 +1796,17 @@ export interface PayCoinSelection {
/** /**
* Amount that each coin contributes. * Amount that each coin contributes.
*/ */
coinContributions: AmountJson[]; coinContributions: AmountString[];
/** /**
* How much of the wire fees is the customer paying? * How much of the wire fees is the customer paying?
*/ */
customerWireFees: AmountJson; customerWireFees: AmountString;
/** /**
* How much of the deposit fees is the customer paying? * How much of the deposit fees is the customer paying?
*/ */
customerDepositFees: AmountJson; customerDepositFees: AmountString;
} }
export interface InitiatePeerPushPaymentRequest { export interface InitiatePeerPushPaymentRequest {

View File

@ -28,6 +28,7 @@ import {
AgeCommitmentProof, AgeCommitmentProof,
AgeRestriction, AgeRestriction,
AmountJson, AmountJson,
AmountLike,
Amounts, Amounts,
AmountString, AmountString,
BlindedDenominationSignature, BlindedDenominationSignature,
@ -1155,8 +1156,8 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
sessionSecretSeed: refreshSessionSecretSeed, sessionSecretSeed: refreshSessionSecretSeed,
} = req; } = req;
const currency = newCoinDenoms[0].value.currency; const currency = Amounts.currencyOf(newCoinDenoms[0].value);
let valueWithFee = Amounts.getZero(currency); let valueWithFee = Amounts.zeroOfCurrency(currency);
for (const ncd of newCoinDenoms) { for (const ncd of newCoinDenoms) {
const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount; const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount;
@ -1627,21 +1628,22 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
}, },
}; };
function amountToBuffer(amount: AmountJson): Uint8Array { function amountToBuffer(amount: AmountLike): Uint8Array {
const amountJ = Amounts.jsonifyAmount(amount);
const buffer = new ArrayBuffer(8 + 4 + 12); const buffer = new ArrayBuffer(8 + 4 + 12);
const dvbuf = new DataView(buffer); const dvbuf = new DataView(buffer);
const u8buf = new Uint8Array(buffer); const u8buf = new Uint8Array(buffer);
const curr = stringToBytes(amount.currency); const curr = stringToBytes(amountJ.currency);
if (typeof dvbuf.setBigUint64 !== "undefined") { if (typeof dvbuf.setBigUint64 !== "undefined") {
dvbuf.setBigUint64(0, BigInt(amount.value)); dvbuf.setBigUint64(0, BigInt(amountJ.value));
} else { } else {
const arr = bigint(amount.value).toArray(2 ** 8).value; const arr = bigint(amountJ.value).toArray(2 ** 8).value;
let offset = 8 - arr.length; let offset = 8 - arr.length;
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
dvbuf.setUint8(offset++, arr[i]); dvbuf.setUint8(offset++, arr[i]);
} }
} }
dvbuf.setUint32(8, amount.fraction); dvbuf.setUint32(8, amountJ.fraction);
u8buf.set(curr, 8 + 4); u8buf.set(curr, 8 + 4);
return u8buf; return u8buf;

View File

@ -44,8 +44,8 @@ import {
export interface RefreshNewDenomInfo { export interface RefreshNewDenomInfo {
count: number; count: number;
value: AmountJson; value: AmountString;
feeWithdraw: AmountJson; feeWithdraw: AmountString;
denomPub: DenominationPubKey; denomPub: DenominationPubKey;
denomPubHash: string; denomPubHash: string;
} }

View File

@ -47,6 +47,7 @@ import {
UnblindedSignature, UnblindedSignature,
WireInfo, WireInfo,
HashCodeString, HashCodeString,
Amounts,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
describeContents, describeContents,
@ -276,22 +277,22 @@ export interface DenomFees {
/** /**
* Fee for withdrawing. * Fee for withdrawing.
*/ */
feeWithdraw: AmountJson; feeWithdraw: AmountString;
/** /**
* Fee for depositing. * Fee for depositing.
*/ */
feeDeposit: AmountJson; feeDeposit: AmountString;
/** /**
* Fee for refreshing. * Fee for refreshing.
*/ */
feeRefresh: AmountJson; feeRefresh: AmountString;
/** /**
* Fee for refunding. * Fee for refunding.
*/ */
feeRefund: AmountJson; feeRefund: AmountString;
} }
/** /**
@ -393,15 +394,15 @@ export namespace DenominationRecord {
return { return {
denomPub: d.denomPub, denomPub: d.denomPub,
denomPubHash: d.denomPubHash, denomPubHash: d.denomPubHash,
feeDeposit: d.fees.feeDeposit, feeDeposit: Amounts.stringify(d.fees.feeDeposit),
feeRefresh: d.fees.feeRefresh, feeRefresh: Amounts.stringify(d.fees.feeRefresh),
feeRefund: d.fees.feeRefund, feeRefund: Amounts.stringify(d.fees.feeRefund),
feeWithdraw: d.fees.feeWithdraw, feeWithdraw: Amounts.stringify(d.fees.feeWithdraw),
stampExpireDeposit: d.stampExpireDeposit, stampExpireDeposit: d.stampExpireDeposit,
stampExpireLegal: d.stampExpireLegal, stampExpireLegal: d.stampExpireLegal,
stampExpireWithdraw: d.stampExpireWithdraw, stampExpireWithdraw: d.stampExpireWithdraw,
stampStart: d.stampStart, stampStart: d.stampStart,
value: DenominationRecord.getValue(d), value: Amounts.stringify(DenominationRecord.getValue(d)),
exchangeBaseUrl: d.exchangeBaseUrl, exchangeBaseUrl: d.exchangeBaseUrl,
}; };
} }
@ -527,7 +528,7 @@ export interface ExchangeRecord {
* currency. * currency.
* *
* We could use a rowID here, but having the currency in the * We could use a rowID here, but having the currency in the
* details pointer lets us do fewer DB queries sometimes. * details pointer lets us do fewer DB queries
*/ */
detailsPointer: ExchangeDetailsPointer | undefined; detailsPointer: ExchangeDetailsPointer | undefined;
@ -752,12 +753,12 @@ export interface TipRecord {
/** /**
* The tipped amount. * The tipped amount.
*/ */
tipAmountRaw: AmountJson; tipAmountRaw: AmountString;
/** /**
* Effect on the balance (including fees etc). * Effect on the balance (including fees etc).
*/ */
tipAmountEffective: AmountJson; tipAmountEffective: AmountString;
/** /**
* Timestamp, the tip can't be picked up anymore after this deadline. * Timestamp, the tip can't be picked up anymore after this deadline.
@ -854,9 +855,9 @@ export interface RefreshGroupRecord {
// object store for faster updates? // object store for faster updates?
refreshSessionPerCoin: (RefreshSessionRecord | undefined)[]; refreshSessionPerCoin: (RefreshSessionRecord | undefined)[];
inputPerCoin: AmountJson[]; inputPerCoin: AmountString[];
estimatedOutputPerCoin: AmountJson[]; estimatedOutputPerCoin: AmountString[];
/** /**
* Flag for each coin whether refreshing finished. * Flag for each coin whether refreshing finished.
@ -888,7 +889,7 @@ export interface RefreshSessionRecord {
* Sum of the value of denominations we want * Sum of the value of denominations we want
* to withdraw in this session, without fees. * to withdraw in this session, without fees.
*/ */
amountRefreshOutput: AmountJson; amountRefreshOutput: AmountString;
/** /**
* Hashed denominations of the newly requested coins. * Hashed denominations of the newly requested coins.
@ -927,9 +928,9 @@ export interface WalletRefundItemCommon {
*/ */
obtainedTime: TalerProtocolTimestamp; obtainedTime: TalerProtocolTimestamp;
refundAmount: AmountJson; refundAmount: AmountString;
refundFee: AmountJson; refundFee: AmountString;
/** /**
* Upper bound on the refresh cost incurred by * Upper bound on the refresh cost incurred by
@ -938,7 +939,7 @@ export interface WalletRefundItemCommon {
* Might be lower in practice when two refunds on the same * Might be lower in practice when two refunds on the same
* coin are refreshed in the same refresh operation. * coin are refreshed in the same refresh operation.
*/ */
totalRefreshCostBound: AmountJson; totalRefreshCostBound: AmountString;
coinPub: string; coinPub: string;
@ -1003,12 +1004,12 @@ export interface WalletContractData {
merchantSig: string; merchantSig: string;
merchantPub: string; merchantPub: string;
merchant: MerchantInfo; merchant: MerchantInfo;
amount: AmountJson; amount: AmountString;
orderId: string; orderId: string;
merchantBaseUrl: string; merchantBaseUrl: string;
summary: string; summary: string;
autoRefund: TalerProtocolDuration | undefined; autoRefund: TalerProtocolDuration | undefined;
maxWireFee: AmountJson; maxWireFee: AmountString;
wireFeeAmortization: number; wireFeeAmortization: number;
payDeadline: TalerProtocolTimestamp; payDeadline: TalerProtocolTimestamp;
refundDeadline: TalerProtocolTimestamp; refundDeadline: TalerProtocolTimestamp;
@ -1017,7 +1018,7 @@ export interface WalletContractData {
timestamp: TalerProtocolTimestamp; timestamp: TalerProtocolTimestamp;
wireMethod: string; wireMethod: string;
wireInfoHash: string; wireInfoHash: string;
maxDepositFee: AmountJson; maxDepositFee: AmountString;
minimumAge?: number; minimumAge?: number;
deliveryDate: TalerProtocolTimestamp | undefined; deliveryDate: TalerProtocolTimestamp | undefined;
deliveryLocation: Location | undefined; deliveryLocation: Location | undefined;
@ -1099,7 +1100,7 @@ export interface ProposalDownloadInfo {
export interface PurchasePayInfo { export interface PurchasePayInfo {
payCoinSelection: PayCoinSelection; payCoinSelection: PayCoinSelection;
totalPayCost: AmountJson; totalPayCost: AmountString;
payCoinSelectionUid: string; payCoinSelectionUid: string;
} }
@ -1216,7 +1217,7 @@ export interface PurchaseRecord {
* How much merchant has refund to be taken but the wallet * How much merchant has refund to be taken but the wallet
* did not picked up yet * did not picked up yet
*/ */
refundAmountAwaiting: AmountJson | undefined; refundAmountAwaiting: AmountString | undefined;
} }
export enum ConfigRecordKey { export enum ConfigRecordKey {
@ -1379,7 +1380,7 @@ export interface WithdrawalGroupRecord {
/** /**
* Amount that was sent by the user to fund the reserve. * Amount that was sent by the user to fund the reserve.
*/ */
instructedAmount: AmountJson; instructedAmount: AmountString;
/** /**
* Amount that was observed when querying the reserve that * Amount that was observed when querying the reserve that
@ -1387,7 +1388,7 @@ export interface WithdrawalGroupRecord {
* *
* Useful for diagnostics. * Useful for diagnostics.
*/ */
reserveBalanceAmount?: AmountJson; reserveBalanceAmount?: AmountString;
/** /**
* Amount including fees (i.e. the amount subtracted from the * Amount including fees (i.e. the amount subtracted from the
@ -1396,7 +1397,7 @@ export interface WithdrawalGroupRecord {
* (Initial amount confirmed by the user, might differ with denomSel * (Initial amount confirmed by the user, might differ with denomSel
* on reselection.) * on reselection.)
*/ */
rawWithdrawalAmount: AmountJson; rawWithdrawalAmount: AmountString;
/** /**
* Amount that will be added to the balance when the withdrawal succeeds. * Amount that will be added to the balance when the withdrawal succeeds.
@ -1404,7 +1405,7 @@ export interface WithdrawalGroupRecord {
* (Initial amount confirmed by the user, might differ with denomSel * (Initial amount confirmed by the user, might differ with denomSel
* on reselection.) * on reselection.)
*/ */
effectiveWithdrawalAmount: AmountJson; effectiveWithdrawalAmount: AmountString;
/** /**
* Denominations selected for withdrawal. * Denominations selected for withdrawal.
@ -1587,9 +1588,9 @@ export interface DepositGroupRecord {
payCoinSelectionUid: string; payCoinSelectionUid: string;
totalPayCost: AmountJson; totalPayCost: AmountString;
effectiveDepositAmount: AmountJson; effectiveDepositAmount: AmountString;
depositedPerCoin: boolean[]; depositedPerCoin: boolean[];

View File

@ -160,7 +160,7 @@ export async function withdrawCoin(args: {
const planchet = await cryptoApi.createPlanchet({ const planchet = await cryptoApi.createPlanchet({
coinIndex: 0, coinIndex: 0,
denomPub: denom.denomPub, denomPub: denom.denomPub,
feeWithdraw: denom.fees.feeWithdraw, feeWithdraw: Amounts.parseOrThrow(denom.fees.feeWithdraw),
reservePriv: reserveKeyPair.reservePriv, reservePriv: reserveKeyPair.reservePriv,
reservePub: reserveKeyPair.reservePub, reservePub: reserveKeyPair.reservePub,
secretSeed: encodeCrock(getRandomBytes(32)), secretSeed: encodeCrock(getRandomBytes(32)),
@ -294,11 +294,11 @@ export async function refreshCoin(req: {
denomPub: x.denomPub, denomPub: x.denomPub,
denomPubHash: x.denomPubHash, denomPubHash: x.denomPubHash,
feeWithdraw: x.fees.feeWithdraw, feeWithdraw: x.fees.feeWithdraw,
value: { value: Amounts.stringify({
currency: x.currency, currency: x.currency,
fraction: x.amountFrac, fraction: x.amountFrac,
value: x.amountVal, value: x.amountVal,
}, }),
})), })),
meltCoinMaxAge: oldCoin.maxAge, meltCoinMaxAge: oldCoin.maxAge,
}); });

View File

@ -104,10 +104,8 @@ async function recoverPayCoinSelection(
const coveredExchanges: Set<string> = new Set(); const coveredExchanges: Set<string> = new Set();
let totalWireFee: AmountJson = Amounts.getZero(contractData.amount.currency); let totalWireFee: AmountJson = Amounts.zeroOfAmount(contractData.amount);
let totalDepositFees: AmountJson = Amounts.getZero( let totalDepositFees: AmountJson = Amounts.zeroOfAmount(contractData.amount);
contractData.amount.currency,
);
for (const coinPub of coinPubs) { for (const coinPub of coinPubs) {
const coinRecord = await tx.coins.get(coinPub); const coinRecord = await tx.coins.get(coinPub);
@ -136,7 +134,7 @@ async function recoverPayCoinSelection(
fee.startStamp <= contractData.timestamp && fee.startStamp <= contractData.timestamp &&
fee.endStamp >= contractData.timestamp fee.endStamp >= contractData.timestamp
) { ) {
wireFee = fee.wireFee; wireFee = Amounts.parseOrThrow(fee.wireFee);
break; break;
} }
} }
@ -156,7 +154,7 @@ async function recoverPayCoinSelection(
if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) { if (Amounts.cmp(contractData.maxWireFee, amortizedWireFee) < 0) {
customerWireFee = amortizedWireFee; customerWireFee = amortizedWireFee;
} else { } else {
customerWireFee = Amounts.getZero(contractData.amount.currency); customerWireFee = Amounts.zeroOfAmount(contractData.amount);
} }
const customerDepositFees = Amounts.sub( const customerDepositFees = Amounts.sub(
@ -166,10 +164,10 @@ async function recoverPayCoinSelection(
return { return {
coinPubs, coinPubs,
coinContributions, coinContributions: coinContributions.map((x) => Amounts.stringify(x)),
paymentAmount: contractData.amount, paymentAmount: Amounts.stringify(contractData.amount),
customerWireFees: customerWireFee, customerWireFees: Amounts.stringify(customerWireFee),
customerDepositFees, customerDepositFees: Amounts.stringify(customerDepositFees),
}; };
} }
@ -183,8 +181,8 @@ async function getDenomSelStateFromBackup(
denomPubHash: string; denomPubHash: string;
count: number; count: number;
}[] = []; }[] = [];
let totalCoinValue = Amounts.getZero(currency); let totalCoinValue = Amounts.zeroOfCurrency(currency);
let totalWithdrawCost = Amounts.getZero(currency); let totalWithdrawCost = Amounts.zeroOfCurrency(currency);
for (const s of sel) { for (const s of sel) {
const d = await tx.denominations.get([exchangeBaseUrl, s.denom_pub_hash]); const d = await tx.denominations.get([exchangeBaseUrl, s.denom_pub_hash]);
checkBackupInvariant(!!d); checkBackupInvariant(!!d);
@ -200,8 +198,8 @@ async function getDenomSelStateFromBackup(
} }
return { return {
selectedDenoms, selectedDenoms,
totalCoinValue, totalCoinValue: Amounts.stringify(totalCoinValue),
totalWithdrawCost, totalWithdrawCost: Amounts.stringify(totalCoinValue),
}; };
} }
@ -380,11 +378,11 @@ export async function importBackup(
for (const fee of backupExchangeDetails.wire_fees) { for (const fee of backupExchangeDetails.wire_fees) {
const w = (wireInfo.feesForType[fee.wire_type] ??= []); const w = (wireInfo.feesForType[fee.wire_type] ??= []);
w.push({ w.push({
closingFee: Amounts.parseOrThrow(fee.closing_fee), closingFee: Amounts.stringify(fee.closing_fee),
endStamp: fee.end_stamp, endStamp: fee.end_stamp,
sig: fee.sig, sig: fee.sig,
startStamp: fee.start_stamp, startStamp: fee.start_stamp,
wireFee: Amounts.parseOrThrow(fee.wire_fee), wireFee: Amounts.stringify(fee.wire_fee),
}); });
} }
let tosAccepted = undefined; let tosAccepted = undefined;
@ -412,9 +410,9 @@ export async function importBackup(
tosCurrentEtag: backupExchangeDetails.tos_accepted_etag || "", tosCurrentEtag: backupExchangeDetails.tos_accepted_etag || "",
tosAccepted, tosAccepted,
globalFees: backupExchangeDetails.global_fees.map((x) => ({ globalFees: backupExchangeDetails.global_fees.map((x) => ({
accountFee: Amounts.parseOrThrow(x.accountFee), accountFee: Amounts.stringify(x.accountFee),
historyFee: Amounts.parseOrThrow(x.historyFee), historyFee: Amounts.stringify(x.historyFee),
purseFee: Amounts.parseOrThrow(x.purseFee), purseFee: Amounts.stringify(x.purseFee),
endDate: x.endDate, endDate: x.endDate,
historyTimeout: x.historyTimeout, historyTimeout: x.historyTimeout,
signature: x.signature, signature: x.signature,
@ -447,16 +445,10 @@ export async function importBackup(
exchangeBaseUrl: backupExchangeDetails.base_url, exchangeBaseUrl: backupExchangeDetails.base_url,
exchangeMasterPub: backupExchangeDetails.master_public_key, exchangeMasterPub: backupExchangeDetails.master_public_key,
fees: { fees: {
feeDeposit: Amounts.parseOrThrow( feeDeposit: Amounts.stringify(backupDenomination.fee_deposit),
backupDenomination.fee_deposit, feeRefresh: Amounts.stringify(backupDenomination.fee_refresh),
), feeRefund: Amounts.stringify(backupDenomination.fee_refund),
feeRefresh: Amounts.parseOrThrow( feeWithdraw: Amounts.stringify(backupDenomination.fee_withdraw),
backupDenomination.fee_refresh,
),
feeRefund: Amounts.parseOrThrow(backupDenomination.fee_refund),
feeWithdraw: Amounts.parseOrThrow(
backupDenomination.fee_withdraw,
),
}, },
isOffered: backupDenomination.is_offered, isOffered: backupDenomination.is_offered,
isRevoked: backupDenomination.is_revoked, isRevoked: backupDenomination.is_revoked,
@ -542,7 +534,7 @@ export async function importBackup(
await tx.withdrawalGroups.put({ await tx.withdrawalGroups.put({
withdrawalGroupId: backupWg.withdrawal_group_id, withdrawalGroupId: backupWg.withdrawal_group_id,
exchangeBaseUrl: backupWg.exchange_base_url, exchangeBaseUrl: backupWg.exchange_base_url,
instructedAmount, instructedAmount: Amounts.stringify(instructedAmount),
secretSeed: backupWg.secret_seed, secretSeed: backupWg.secret_seed,
denomsSel: await getDenomSelStateFromBackup( denomsSel: await getDenomSelStateFromBackup(
tx, tx,
@ -551,10 +543,10 @@ export async function importBackup(
backupWg.selected_denoms, backupWg.selected_denoms,
), ),
denomSelUid: backupWg.selected_denoms_uid, denomSelUid: backupWg.selected_denoms_uid,
rawWithdrawalAmount: Amounts.parseOrThrow( rawWithdrawalAmount: Amounts.stringify(
backupWg.raw_withdrawal_amount, backupWg.raw_withdrawal_amount,
), ),
effectiveWithdrawalAmount: Amounts.parseOrThrow( effectiveWithdrawalAmount: Amounts.stringify(
backupWg.effective_withdrawal_amount, backupWg.effective_withdrawal_amount,
), ),
reservePriv: backupWg.reserve_priv, reservePriv: backupWg.reserve_priv,
@ -618,10 +610,10 @@ export async function importBackup(
coinPub: backupRefund.coin_pub, coinPub: backupRefund.coin_pub,
executionTime: backupRefund.execution_time, executionTime: backupRefund.execution_time,
obtainedTime: backupRefund.obtained_time, obtainedTime: backupRefund.obtained_time,
refundAmount: Amounts.parseOrThrow(backupRefund.refund_amount), refundAmount: Amounts.stringify(backupRefund.refund_amount),
refundFee: denom.fees.feeRefund, refundFee: Amounts.stringify(denom.fees.feeRefund),
rtransactionId: backupRefund.rtransaction_id, rtransactionId: backupRefund.rtransaction_id,
totalRefreshCostBound: Amounts.parseOrThrow( totalRefreshCostBound: Amounts.stringify(
backupRefund.total_refresh_cost_bound, backupRefund.total_refresh_cost_bound,
), ),
}; };
@ -658,7 +650,7 @@ export async function importBackup(
if (parsedContractTerms.max_wire_fee) { if (parsedContractTerms.max_wire_fee) {
maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee);
} else { } else {
maxWireFee = Amounts.getZero(amount.currency); maxWireFee = Amounts.zeroOfCurrency(amount.currency);
} }
const download: ProposalDownloadInfo = { const download: ProposalDownloadInfo = {
contractTermsHash, contractTermsHash,
@ -682,7 +674,7 @@ export async function importBackup(
backupPurchase.pay_info, backupPurchase.pay_info,
), ),
payCoinSelectionUid: backupPurchase.pay_info.pay_coins_uid, payCoinSelectionUid: backupPurchase.pay_info.pay_coins_uid,
totalPayCost: Amounts.parseOrThrow( totalPayCost: Amounts.stringify(
backupPurchase.pay_info.total_pay_cost, backupPurchase.pay_info.total_pay_cost,
), ),
}; };
@ -776,7 +768,7 @@ export async function importBackup(
count: x.count, count: x.count,
denomPubHash: x.denom_pub_hash, denomPubHash: x.denom_pub_hash,
})), })),
amountRefreshOutput: denomSel.totalCoinValue, amountRefreshOutput: Amounts.stringify(denomSel.totalCoinValue),
}); });
} else { } else {
refreshSessionPerCoin.push(undefined); refreshSessionPerCoin.push(undefined);
@ -797,11 +789,11 @@ export async function importBackup(
operationStatus: backupRefreshGroup.timestamp_finish operationStatus: backupRefreshGroup.timestamp_finish
? RefreshOperationStatus.Finished ? RefreshOperationStatus.Finished
: RefreshOperationStatus.Pending, : RefreshOperationStatus.Pending,
inputPerCoin: backupRefreshGroup.old_coins.map((x) => inputPerCoin: backupRefreshGroup.old_coins.map(
Amounts.parseOrThrow(x.input_amount), (x) => x.input_amount,
), ),
estimatedOutputPerCoin: backupRefreshGroup.old_coins.map((x) => estimatedOutputPerCoin: backupRefreshGroup.old_coins.map(
Amounts.parseOrThrow(x.estimated_output_amount), (x) => x.estimated_output_amount,
), ),
refreshSessionPerCoin, refreshSessionPerCoin,
}); });
@ -834,8 +826,8 @@ export async function importBackup(
merchantTipId: backupTip.merchant_tip_id, merchantTipId: backupTip.merchant_tip_id,
pickedUpTimestamp: backupTip.timestamp_finished, pickedUpTimestamp: backupTip.timestamp_finished,
secretSeed: backupTip.secret_seed, secretSeed: backupTip.secret_seed,
tipAmountEffective: denomsSel.totalCoinValue, tipAmountEffective: Amounts.stringify(denomsSel.totalCoinValue),
tipAmountRaw, tipAmountRaw: Amounts.stringify(tipAmountRaw),
tipExpiration: backupTip.timestamp_expiration, tipExpiration: backupTip.timestamp_expiration,
walletTipId: backupTip.wallet_tip_id, walletTipId: backupTip.wallet_tip_id,
denomSelUid: backupTip.selected_denoms_uid, denomSelUid: backupTip.selected_denoms_uid,

View File

@ -57,9 +57,9 @@ export async function getBalancesInsideTransaction(
const b = balanceStore[currency]; const b = balanceStore[currency];
if (!b) { if (!b) {
balanceStore[currency] = { balanceStore[currency] = {
available: Amounts.getZero(currency), available: Amounts.zeroOfCurrency(currency),
pendingIncoming: Amounts.getZero(currency), pendingIncoming: Amounts.zeroOfCurrency(currency),
pendingOutgoing: Amounts.getZero(currency), pendingOutgoing: Amounts.zeroOfCurrency(currency),
}; };
} }
return balanceStore[currency]; return balanceStore[currency];
@ -85,7 +85,10 @@ export async function getBalancesInsideTransaction(
for (let i = 0; i < r.oldCoinPubs.length; i++) { for (let i = 0; i < r.oldCoinPubs.length; i++) {
const session = r.refreshSessionPerCoin[i]; const session = r.refreshSessionPerCoin[i];
if (session) { if (session) {
const b = initBalance(session.amountRefreshOutput.currency); const currency = Amounts.parseOrThrow(
session.amountRefreshOutput,
).currency;
const b = initBalance(currency);
// We are always assuming the refresh will succeed, thus we // We are always assuming the refresh will succeed, thus we
// report the output as available balance. // report the output as available balance.
b.available = Amounts.add( b.available = Amounts.add(
@ -93,7 +96,8 @@ export async function getBalancesInsideTransaction(
session.amountRefreshOutput, session.amountRefreshOutput,
).amount; ).amount;
} else { } else {
const b = initBalance(r.inputPerCoin[i].currency); const currency = Amounts.parseOrThrow(r.inputPerCoin[i]).currency;
const b = initBalance(currency);
b.available = Amounts.add( b.available = Amounts.add(
b.available, b.available,
r.estimatedOutputPerCoin[i], r.estimatedOutputPerCoin[i],
@ -106,7 +110,7 @@ export async function getBalancesInsideTransaction(
if (wds.timestampFinish) { if (wds.timestampFinish) {
return; return;
} }
const b = initBalance(wds.denomsSel.totalWithdrawCost.currency); const b = initBalance(Amounts.currencyOf(wds.denomsSel.totalWithdrawCost));
b.pendingIncoming = Amounts.add( b.pendingIncoming = Amounts.add(
b.pendingIncoming, b.pendingIncoming,
wds.denomsSel.totalCoinValue, wds.denomsSel.totalCoinValue,

View File

@ -160,7 +160,7 @@ export async function spendCoins(
throw Error("not enough remaining balance on coin for payment"); throw Error("not enough remaining balance on coin for payment");
} }
refreshCoinPubs.push({ refreshCoinPubs.push({
amount: remaining.amount, amount: Amounts.stringify(remaining.amount),
coinPub: coin.coinPub, coinPub: coin.coinPub,
}); });
checkDbInvariant(!!coinAvailability); checkDbInvariant(!!coinAvailability);

View File

@ -348,10 +348,10 @@ export async function prepareDepositGroup(
auditors: contractData.allowedAuditors, auditors: contractData.allowedAuditors,
exchanges: contractData.allowedExchanges, exchanges: contractData.allowedExchanges,
wireMethod: contractData.wireMethod, wireMethod: contractData.wireMethod,
contractTermsAmount: contractData.amount, contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
depositFeeLimit: contractData.maxDepositFee, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
wireFeeLimit: contractData.maxWireFee, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee),
prevPayCoins: [], prevPayCoins: [],
}); });
@ -445,10 +445,10 @@ export async function createDepositGroup(
auditors: contractData.allowedAuditors, auditors: contractData.allowedAuditors,
exchanges: contractData.allowedExchanges, exchanges: contractData.allowedExchanges,
wireMethod: contractData.wireMethod, wireMethod: contractData.wireMethod,
contractTermsAmount: contractData.amount, contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
depositFeeLimit: contractData.maxDepositFee, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
wireFeeLimit: contractData.maxWireFee, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee),
prevPayCoins: [], prevPayCoins: [],
}); });
@ -479,8 +479,8 @@ export async function createDepositGroup(
depositedPerCoin: payCoinSel.coinPubs.map(() => false), depositedPerCoin: payCoinSel.coinPubs.map(() => false),
merchantPriv: merchantPair.priv, merchantPriv: merchantPair.priv,
merchantPub: merchantPair.pub, merchantPub: merchantPair.pub,
totalPayCost: totalDepositCost, totalPayCost: Amounts.stringify(totalDepositCost),
effectiveDepositAmount, effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount),
wire: { wire: {
payto_uri: req.depositPaytoUri, payto_uri: req.depositPaytoUri,
salt: wireSalt, salt: wireSalt,
@ -501,7 +501,9 @@ export async function createDepositGroup(
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:deposit:${depositGroup.depositGroupId}`, allocationId: `txn:deposit:${depositGroup.depositGroupId}`,
coinPubs: payCoinSel.coinPubs, coinPubs: payCoinSel.coinPubs,
contributions: payCoinSel.coinContributions, contributions: payCoinSel.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
),
refreshReason: RefreshReason.PayDeposit, refreshReason: RefreshReason.PayDeposit,
}); });
await tx.depositGroups.put(depositGroup); await tx.depositGroups.put(depositGroup);
@ -543,8 +545,8 @@ export async function getEffectiveDepositAmount(
if (!denom) { if (!denom) {
throw Error("can't find denomination to calculate deposit amount"); throw Error("can't find denomination to calculate deposit amount");
} }
amt.push(pcs.coinContributions[i]); amt.push(Amounts.parseOrThrow(pcs.coinContributions[i]));
fees.push(denom.feeDeposit); fees.push(Amounts.parseOrThrow(denom.feeDeposit));
exchangeSet.add(coin.exchangeBaseUrl); exchangeSet.add(coin.exchangeBaseUrl);
} }
@ -565,7 +567,7 @@ export async function getEffectiveDepositAmount(
); );
})?.wireFee; })?.wireFee;
if (fee) { if (fee) {
fees.push(fee); fees.push(Amounts.parseOrThrow(fee));
} }
} }
}); });
@ -604,7 +606,7 @@ export async function getTotalFeesForDepositAmount(
if (!denom) { if (!denom) {
throw Error("can't find denomination to calculate deposit amount"); throw Error("can't find denomination to calculate deposit amount");
} }
coinFee.push(denom.feeDeposit); coinFee.push(Amounts.parseOrThrow(denom.feeDeposit));
exchangeSet.add(coin.exchangeBaseUrl); exchangeSet.add(coin.exchangeBaseUrl);
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
@ -638,7 +640,7 @@ export async function getTotalFeesForDepositAmount(
}, },
)?.wireFee; )?.wireFee;
if (fee) { if (fee) {
wireFee.push(fee); wireFee.push(Amounts.parseOrThrow(fee));
} }
} }
}); });

View File

@ -98,10 +98,10 @@ function denominationRecordFromKeys(
exchangeBaseUrl, exchangeBaseUrl,
exchangeMasterPub, exchangeMasterPub,
fees: { fees: {
feeDeposit: Amounts.parseOrThrow(denomIn.fee_deposit), feeDeposit: Amounts.stringify(denomIn.fee_deposit),
feeRefresh: Amounts.parseOrThrow(denomIn.fee_refresh), feeRefresh: Amounts.stringify(denomIn.fee_refresh),
feeRefund: Amounts.parseOrThrow(denomIn.fee_refund), feeRefund: Amounts.stringify(denomIn.fee_refund),
feeWithdraw: Amounts.parseOrThrow(denomIn.fee_withdraw), feeWithdraw: Amounts.stringify(denomIn.fee_withdraw),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -267,11 +267,11 @@ async function validateWireInfo(
const startStamp = x.start_date; const startStamp = x.start_date;
const endStamp = x.end_date; const endStamp = x.end_date;
const fee: WireFee = { const fee: WireFee = {
closingFee: Amounts.parseOrThrow(x.closing_fee), closingFee: Amounts.stringify(x.closing_fee),
endStamp, endStamp,
sig: x.sig, sig: x.sig,
startStamp, startStamp,
wireFee: Amounts.parseOrThrow(x.wire_fee), wireFee: Amounts.stringify(x.wire_fee),
}; };
let isValid = false; let isValid = false;
if (ws.insecureTrustExchange) { if (ws.insecureTrustExchange) {
@ -321,9 +321,9 @@ async function validateGlobalFees(
throw Error("exchange global fees signature invalid: " + gf.master_sig); throw Error("exchange global fees signature invalid: " + gf.master_sig);
} }
egf.push({ egf.push({
accountFee: Amounts.parseOrThrow(gf.account_fee), accountFee: Amounts.stringify(gf.account_fee),
historyFee: Amounts.parseOrThrow(gf.history_fee), historyFee: Amounts.stringify(gf.history_fee),
purseFee: Amounts.parseOrThrow(gf.purse_fee), purseFee: Amounts.stringify(gf.purse_fee),
startDate: gf.start_date, startDate: gf.start_date,
endDate: gf.end_date, endDate: gf.end_date,
signature: gf.master_sig, signature: gf.master_sig,

View File

@ -182,10 +182,10 @@ export async function getTotalPaymentCost(
DenominationRecord.toDenomInfo(denom), DenominationRecord.toDenomInfo(denom),
amountLeft, amountLeft,
); );
costs.push(pcs.coinContributions[i]); costs.push(Amounts.parseOrThrow(pcs.coinContributions[i]));
costs.push(refreshCost); costs.push(refreshCost);
} }
const zero = Amounts.getZero(pcs.paymentAmount.currency); const zero = Amounts.zeroOfAmount(pcs.paymentAmount);
return Amounts.sum([zero, ...costs]).amount; return Amounts.sum([zero, ...costs]).amount;
}); });
} }
@ -307,10 +307,10 @@ export function extractContractData(
if (parsedContractTerms.max_wire_fee) { if (parsedContractTerms.max_wire_fee) {
maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee); maxWireFee = Amounts.parseOrThrow(parsedContractTerms.max_wire_fee);
} else { } else {
maxWireFee = Amounts.getZero(amount.currency); maxWireFee = Amounts.zeroOfCurrency(amount.currency);
} }
return { return {
amount, amount: Amounts.stringify(amount),
contractTermsHash: contractTermsHash, contractTermsHash: contractTermsHash,
fulfillmentUrl: parsedContractTerms.fulfillment_url ?? "", fulfillmentUrl: parsedContractTerms.fulfillment_url ?? "",
merchantBaseUrl: parsedContractTerms.merchant_base_url, merchantBaseUrl: parsedContractTerms.merchant_base_url,
@ -319,7 +319,7 @@ export function extractContractData(
orderId: parsedContractTerms.order_id, orderId: parsedContractTerms.order_id,
summary: parsedContractTerms.summary, summary: parsedContractTerms.summary,
autoRefund: parsedContractTerms.auto_refund, autoRefund: parsedContractTerms.auto_refund,
maxWireFee, maxWireFee: Amounts.stringify(maxWireFee),
payDeadline: parsedContractTerms.pay_deadline, payDeadline: parsedContractTerms.pay_deadline,
refundDeadline: parsedContractTerms.refund_deadline, refundDeadline: parsedContractTerms.refund_deadline,
wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1, wireFeeAmortization: parsedContractTerms.wire_fee_amortization || 1,
@ -334,7 +334,7 @@ export function extractContractData(
timestamp: parsedContractTerms.timestamp, timestamp: parsedContractTerms.timestamp,
wireMethod: parsedContractTerms.wire_method, wireMethod: parsedContractTerms.wire_method,
wireInfoHash: parsedContractTerms.h_wire, wireInfoHash: parsedContractTerms.h_wire,
maxDepositFee: Amounts.parseOrThrow(parsedContractTerms.max_fee), maxDepositFee: Amounts.stringify(parsedContractTerms.max_fee),
merchant: parsedContractTerms.merchant, merchant: parsedContractTerms.merchant,
products: parsedContractTerms.products, products: parsedContractTerms.products,
summaryI18n: parsedContractTerms.summary_i18n, summaryI18n: parsedContractTerms.summary_i18n,
@ -539,7 +539,7 @@ export async function processDownloadProposal(
p.download = { p.download = {
contractTermsHash, contractTermsHash,
contractTermsMerchantSig: contractData.merchantSig, contractTermsMerchantSig: contractData.merchantSig,
currency: contractData.amount.currency, currency: Amounts.currencyOf(contractData.amount),
fulfillmentUrl: contractData.fulfillmentUrl, fulfillmentUrl: contractData.fulfillmentUrl,
}; };
await tx.contractTerms.put({ await tx.contractTerms.put({
@ -825,9 +825,9 @@ async function handleInsufficientFunds(
} }
prevPayCoins.push({ prevPayCoins.push({
coinPub, coinPub,
contribution: contrib, contribution: Amounts.parseOrThrow(contrib),
exchangeBaseUrl: coin.exchangeBaseUrl, exchangeBaseUrl: coin.exchangeBaseUrl,
feeDeposit: denom.fees.feeDeposit, feeDeposit: Amounts.parseOrThrow(denom.fees.feeDeposit),
}); });
} }
}); });
@ -836,10 +836,10 @@ async function handleInsufficientFunds(
auditors: contractData.allowedAuditors, auditors: contractData.allowedAuditors,
exchanges: contractData.allowedExchanges, exchanges: contractData.allowedExchanges,
wireMethod: contractData.wireMethod, wireMethod: contractData.wireMethod,
contractTermsAmount: contractData.amount, contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
depositFeeLimit: contractData.maxDepositFee, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
wireFeeLimit: contractData.maxWireFee, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee),
prevPayCoins, prevPayCoins,
requiredMinimumAge: contractData.minimumAge, requiredMinimumAge: contractData.minimumAge,
}); });
@ -875,7 +875,9 @@ async function handleInsufficientFunds(
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:proposal:${p.proposalId}`, allocationId: `txn:proposal:${p.proposalId}`,
coinPubs: payInfo.payCoinSelection.coinPubs, coinPubs: payInfo.payCoinSelection.coinPubs,
contributions: payInfo.payCoinSelection.coinContributions, contributions: payInfo.payCoinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
),
refreshReason: RefreshReason.PayMerchant, refreshReason: RefreshReason.PayMerchant,
}); });
}); });
@ -1068,7 +1070,7 @@ export function selectGreedy(
wireFeesPerExchange, wireFeesPerExchange,
wireFeeAmortization, wireFeeAmortization,
aci.exchangeBaseUrl, aci.exchangeBaseUrl,
aci.feeDeposit, Amounts.parseOrThrow(aci.feeDeposit),
); );
let coinSpend = Amounts.max( let coinSpend = Amounts.max(
@ -1190,8 +1192,8 @@ export async function selectPayCoinsNew(
amountPayRemaining: contractTermsAmount, amountPayRemaining: contractTermsAmount,
amountWireFeeLimitRemaining: wireFeeLimit, amountWireFeeLimitRemaining: wireFeeLimit,
amountDepositFeeLimitRemaining: depositFeeLimit, amountDepositFeeLimitRemaining: depositFeeLimit,
customerDepositFees: Amounts.getZero(currency), customerDepositFees: Amounts.zeroOfCurrency(currency),
customerWireFees: Amounts.getZero(currency), customerWireFees: Amounts.zeroOfCurrency(currency),
wireFeeCoveredForExchange: new Set(), wireFeeCoveredForExchange: new Set(),
}; };
@ -1269,11 +1271,11 @@ export async function selectPayCoinsNew(
}); });
return { return {
paymentAmount: contractTermsAmount, paymentAmount: Amounts.stringify(contractTermsAmount),
coinContributions, coinContributions: coinContributions.map((x) => Amounts.stringify(x)),
coinPubs, coinPubs,
customerDepositFees: tally.customerDepositFees, customerDepositFees: Amounts.stringify(tally.customerDepositFees),
customerWireFees: tally.customerWireFees, customerWireFees: Amounts.stringify(tally.customerWireFees),
}; };
} }
@ -1326,10 +1328,10 @@ export async function checkPaymentByProposalId(
const res = await selectPayCoinsNew(ws, { const res = await selectPayCoinsNew(ws, {
auditors: contractData.allowedAuditors, auditors: contractData.allowedAuditors,
exchanges: contractData.allowedExchanges, exchanges: contractData.allowedExchanges,
contractTermsAmount: contractData.amount, contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
depositFeeLimit: contractData.maxDepositFee, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
wireFeeLimit: contractData.maxWireFee, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee),
prevPayCoins: [], prevPayCoins: [],
requiredMinimumAge: contractData.minimumAge, requiredMinimumAge: contractData.minimumAge,
wireMethod: contractData.wireMethod, wireMethod: contractData.wireMethod,
@ -1531,10 +1533,10 @@ export async function generateDepositPermissions(
denomKeyType: denom.denomPub.cipher, denomKeyType: denom.denomPub.cipher,
denomSig: coin.denomSig, denomSig: coin.denomSig,
exchangeBaseUrl: coin.exchangeBaseUrl, exchangeBaseUrl: coin.exchangeBaseUrl,
feeDeposit: denom.fees.feeDeposit, feeDeposit: Amounts.parseOrThrow(denom.fees.feeDeposit),
merchantPub: contractData.merchantPub, merchantPub: contractData.merchantPub,
refundDeadline: contractData.refundDeadline, refundDeadline: contractData.refundDeadline,
spendAmount: payCoinSel.coinContributions[i], spendAmount: Amounts.parseOrThrow(payCoinSel.coinContributions[i]),
timestamp: contractData.timestamp, timestamp: contractData.timestamp,
wireInfoHash, wireInfoHash,
ageCommitmentProof: coin.ageCommitmentProof, ageCommitmentProof: coin.ageCommitmentProof,
@ -1684,10 +1686,10 @@ export async function confirmPay(
auditors: contractData.allowedAuditors, auditors: contractData.allowedAuditors,
exchanges: contractData.allowedExchanges, exchanges: contractData.allowedExchanges,
wireMethod: contractData.wireMethod, wireMethod: contractData.wireMethod,
contractTermsAmount: contractData.amount, contractTermsAmount: Amounts.parseOrThrow(contractData.amount),
depositFeeLimit: contractData.maxDepositFee, depositFeeLimit: Amounts.parseOrThrow(contractData.maxDepositFee),
wireFeeAmortization: contractData.wireFeeAmortization ?? 1, wireFeeAmortization: contractData.wireFeeAmortization ?? 1,
wireFeeLimit: contractData.maxWireFee, wireFeeLimit: Amounts.parseOrThrow(contractData.maxWireFee),
prevPayCoins: [], prevPayCoins: [],
requiredMinimumAge: contractData.minimumAge, requiredMinimumAge: contractData.minimumAge,
forcedSelection: forcedCoinSel, forcedSelection: forcedCoinSel,
@ -1742,7 +1744,7 @@ export async function confirmPay(
p.payInfo = { p.payInfo = {
payCoinSelection: coinSelection, payCoinSelection: coinSelection,
payCoinSelectionUid: encodeCrock(getRandomBytes(16)), payCoinSelectionUid: encodeCrock(getRandomBytes(16)),
totalPayCost: payCostInfo, totalPayCost: Amounts.stringify(payCostInfo),
}; };
p.lastSessionId = sessionId; p.lastSessionId = sessionId;
p.timestampAccept = TalerProtocolTimestamp.now(); p.timestampAccept = TalerProtocolTimestamp.now();
@ -1751,7 +1753,9 @@ export async function confirmPay(
await spendCoins(ws, tx, { await spendCoins(ws, tx, {
allocationId: `txn:proposal:${p.proposalId}`, allocationId: `txn:proposal:${p.proposalId}`,
coinPubs: coinSelection.coinPubs, coinPubs: coinSelection.coinPubs,
contributions: coinSelection.coinContributions, contributions: coinSelection.coinContributions.map((x) =>
Amounts.parseOrThrow(x),
),
refreshReason: RefreshReason.PayMerchant, refreshReason: RefreshReason.PayMerchant,
}); });
break; break;
@ -2131,15 +2135,18 @@ async function applySuccessfulRefund(
amountLeft, amountLeft,
); );
refreshCoinsMap[coin.coinPub] = { coinPub: coin.coinPub, amount: amountLeft }; refreshCoinsMap[coin.coinPub] = {
coinPub: coin.coinPub,
amount: Amounts.stringify(amountLeft),
};
p.refunds[refundKey] = { p.refunds[refundKey] = {
type: RefundState.Applied, type: RefundState.Applied,
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.stringify(r.refund_amount),
refundFee: denom.fees.feeRefund, refundFee: Amounts.stringify(denom.fees.feeRefund),
totalRefreshCostBound, totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound),
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
}; };
@ -2189,9 +2196,9 @@ async function storePendingRefund(
type: RefundState.Pending, type: RefundState.Pending,
obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()), obtainedTime: AbsoluteTime.toTimestamp(AbsoluteTime.now()),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.stringify(r.refund_amount),
refundFee: denom.fees.feeRefund, refundFee: Amounts.stringify(denom.fees.feeRefund),
totalRefreshCostBound, totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound),
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
}; };
@ -2241,9 +2248,9 @@ async function storeFailedRefund(
type: RefundState.Failed, type: RefundState.Failed,
obtainedTime: TalerProtocolTimestamp.now(), obtainedTime: TalerProtocolTimestamp.now(),
executionTime: r.execution_time, executionTime: r.execution_time,
refundAmount: Amounts.parseOrThrow(r.refund_amount), refundAmount: Amounts.stringify(r.refund_amount),
refundFee: denom.fees.feeRefund, refundFee: Amounts.stringify(denom.fees.feeRefund),
totalRefreshCostBound, totalRefreshCostBound: Amounts.stringify(totalRefreshCostBound),
coinPub: r.coin_pub, coinPub: r.coin_pub,
rtransactionId: r.rtransaction_id, rtransactionId: r.rtransaction_id,
}; };
@ -2274,13 +2281,13 @@ async function storeFailedRefund(
let contrib: AmountJson | undefined; let contrib: AmountJson | undefined;
for (let i = 0; i < payCoinSelection.coinPubs.length; i++) { for (let i = 0; i < payCoinSelection.coinPubs.length; i++) {
if (payCoinSelection.coinPubs[i] === r.coin_pub) { if (payCoinSelection.coinPubs[i] === r.coin_pub) {
contrib = payCoinSelection.coinContributions[i]; contrib = Amounts.parseOrThrow(payCoinSelection.coinContributions[i]);
} }
} }
// FIXME: Is this case tested?! // FIXME: Is this case tested?!
refreshCoinsMap[coin.coinPub] = { refreshCoinsMap[coin.coinPub] = {
coinPub: coin.coinPub, coinPub: coin.coinPub,
amount: amountLeft, amount: Amounts.stringify(amountLeft),
}; };
await tx.coins.put(coin); await tx.coins.put(coin);
} }
@ -2417,10 +2424,8 @@ async function calculateRefundSummary(
p: PurchaseRecord, p: PurchaseRecord,
): Promise<RefundSummary> { ): Promise<RefundSummary> {
const download = await expectProposalDownload(ws, p); const download = await expectProposalDownload(ws, p);
let amountRefundGranted = Amounts.getZero( let amountRefundGranted = Amounts.zeroOfAmount(download.contractData.amount);
download.contractData.amount.currency, let amountRefundGone = Amounts.zeroOfAmount(download.contractData.amount);
);
let amountRefundGone = Amounts.getZero(download.contractData.amount.currency);
let pendingAtExchange = false; let pendingAtExchange = false;
@ -2454,7 +2459,7 @@ async function calculateRefundSummary(
} }
}); });
return { return {
amountEffectivePaid: payInfo.totalPayCost, amountEffectivePaid: Amounts.parseOrThrow(payInfo.totalPayCost),
amountRefundGone, amountRefundGone,
amountRefundGranted, amountRefundGranted,
pendingAtExchange, pendingAtExchange,
@ -2598,7 +2603,7 @@ async function queryAndSaveAwaitingRefund(
); );
if (!orderStatus.refunded) { if (!orderStatus.refunded) {
// Wait for retry ... // Wait for retry ...
return Amounts.getZero(download.contractData.amount.currency); return Amounts.zeroOfAmount(download.contractData.amount);
} }
const refundAwaiting = Amounts.sub( const refundAwaiting = Amounts.sub(
@ -2618,7 +2623,7 @@ async function queryAndSaveAwaitingRefund(
logger.warn("purchase does not exist anymore"); logger.warn("purchase does not exist anymore");
return; return;
} }
p.refundAmountAwaiting = refundAwaiting; p.refundAmountAwaiting = Amounts.stringify(refundAwaiting);
await tx.purchases.put(p); await tx.purchases.put(p);
}); });
} }

View File

@ -158,8 +158,8 @@ export async function selectPeerCoins(
} }
coinInfos.push({ coinInfos.push({
coinPub: coin.coinPub, coinPub: coin.coinPub,
feeDeposit: denom.feeDeposit, feeDeposit: Amounts.parseOrThrow(denom.feeDeposit),
value: denom.value, value: Amounts.parseOrThrow(denom.value),
denomPubHash: denom.denomPubHash, denomPubHash: denom.denomPubHash,
coinPriv: coin.coinPriv, coinPriv: coin.coinPriv,
denomSig: coin.denomSig, denomSig: coin.denomSig,
@ -175,8 +175,8 @@ export async function selectPeerCoins(
-Amounts.cmp(o1.value, o2.value) || -Amounts.cmp(o1.value, o2.value) ||
strcmp(o1.denomPubHash, o2.denomPubHash), strcmp(o1.denomPubHash, o2.denomPubHash),
); );
let amountAcc = Amounts.getZero(instructedAmount.currency); let amountAcc = Amounts.zeroOfCurrency(instructedAmount.currency);
let depositFeesAcc = Amounts.getZero(instructedAmount.currency); let depositFeesAcc = Amounts.zeroOfCurrency(instructedAmount.currency);
const resCoins: { const resCoins: {
coinPub: string; coinPub: string;
coinPriv: string; coinPriv: string;
@ -553,7 +553,7 @@ export async function acceptPeerPushPayment(
mergeTimestamp: mergeTimestamp, mergeTimestamp: mergeTimestamp,
purseAmount: Amounts.stringify(amount), purseAmount: Amounts.stringify(amount),
purseExpiration: contractTerms.purse_expiration, purseExpiration: contractTerms.purse_expiration,
purseFee: Amounts.stringify(Amounts.getZero(amount.currency)), purseFee: Amounts.stringify(Amounts.zeroOfCurrency(amount.currency)),
pursePub: peerInc.pursePub, pursePub: peerInc.pursePub,
reservePayto, reservePayto,
reservePriv: mergeReserveInfo.reservePriv, reservePriv: mergeReserveInfo.reservePriv,
@ -796,7 +796,7 @@ export async function initiatePeerPullPayment(
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms); const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
const purseFee = Amounts.stringify( const purseFee = Amounts.stringify(
Amounts.getZero(Amounts.parseOrThrow(req.amount).currency), Amounts.zeroOfCurrency(Amounts.parseOrThrow(req.amount).currency),
); );
const sigRes = await ws.cryptoApi.signReservePurseCreate({ const sigRes = await ws.cryptoApi.signReservePurseCreate({

View File

@ -291,7 +291,7 @@ async function recoupRefreshCoin(
).amount; ).amount;
recoupGroup.scheduleRefreshCoins.push({ recoupGroup.scheduleRefreshCoins.push({
coinPub: oldCoin.coinPub, coinPub: oldCoin.coinPub,
amount: residualAmount, amount: Amounts.stringify(residualAmount),
}); });
} }
await tx.coins.put(revokedCoin); await tx.coins.put(revokedCoin);

View File

@ -110,7 +110,7 @@ export function getTotalRefreshCost(
const denomMap = Object.fromEntries(denoms.map((x) => [x.denomPubHash, x])); const denomMap = Object.fromEntries(denoms.map((x) => [x.denomPubHash, x]));
const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, denoms); const withdrawDenoms = selectWithdrawalDenominations(withdrawAmount, denoms);
const resultingAmount = Amounts.add( const resultingAmount = Amounts.add(
Amounts.getZero(withdrawAmount.currency), Amounts.zeroOfCurrency(withdrawAmount.currency),
...withdrawDenoms.selectedDenoms.map( ...withdrawDenoms.selectedDenoms.map(
(d) => (d) =>
Amounts.mult( Amounts.mult(
@ -273,7 +273,7 @@ async function refreshCreateSession(
count: x.count, count: x.count,
denomPubHash: x.denomPubHash, denomPubHash: x.denomPubHash,
})), })),
amountRefreshOutput: newCoinDenoms.totalCoinValue, amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue),
}; };
await tx.refreshGroups.put(rg); await tx.refreshGroups.put(rg);
}); });
@ -340,7 +340,7 @@ async function refreshMelt(
denomPub: newDenom.denomPub, denomPub: newDenom.denomPub,
denomPubHash: newDenom.denomPubHash, denomPubHash: newDenom.denomPubHash,
feeWithdraw: newDenom.feeWithdraw, feeWithdraw: newDenom.feeWithdraw,
value: newDenom.value, value: Amounts.stringify(newDenom.value),
}); });
} }
return { newCoinDenoms, oldCoin, oldDenom, refreshGroup, refreshSession }; return { newCoinDenoms, oldCoin, oldDenom, refreshGroup, refreshSession };
@ -368,7 +368,7 @@ async function refreshMelt(
meltCoinDenomPubHash: oldCoin.denomPubHash, meltCoinDenomPubHash: oldCoin.denomPubHash,
meltCoinPriv: oldCoin.coinPriv, meltCoinPriv: oldCoin.coinPriv,
meltCoinPub: oldCoin.coinPub, meltCoinPub: oldCoin.coinPub,
feeRefresh: oldDenom.feeRefresh, feeRefresh: Amounts.parseOrThrow(oldDenom.feeRefresh),
meltCoinMaxAge: oldCoin.maxAge, meltCoinMaxAge: oldCoin.maxAge,
meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof, meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof,
newCoinDenoms, newCoinDenoms,
@ -584,7 +584,7 @@ async function refreshReveal(
denomPub: newDenom.denomPub, denomPub: newDenom.denomPub,
denomPubHash: newDenom.denomPubHash, denomPubHash: newDenom.denomPubHash,
feeWithdraw: newDenom.feeWithdraw, feeWithdraw: newDenom.feeWithdraw,
value: newDenom.value, value: Amounts.stringify(newDenom.value),
}); });
} }
return { return {
@ -626,7 +626,7 @@ async function refreshReveal(
meltCoinDenomPubHash: oldCoin.denomPubHash, meltCoinDenomPubHash: oldCoin.denomPubHash,
meltCoinPriv: oldCoin.coinPriv, meltCoinPriv: oldCoin.coinPriv,
meltCoinPub: oldCoin.coinPub, meltCoinPub: oldCoin.coinPub,
feeRefresh: oldDenom.feeRefresh, feeRefresh: Amounts.parseOrThrow(oldDenom.feeRefresh),
newCoinDenoms, newCoinDenoms,
meltCoinMaxAge: oldCoin.maxAge, meltCoinMaxAge: oldCoin.maxAge,
meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof, meltCoinAgeCommitmentProof: oldCoin.ageCommitmentProof,
@ -922,10 +922,14 @@ export async function createRefreshGroup(
assertUnreachable(coin.status); assertUnreachable(coin.status);
} }
const refreshAmount = ocp.amount; const refreshAmount = ocp.amount;
inputPerCoin.push(refreshAmount); inputPerCoin.push(Amounts.parseOrThrow(refreshAmount));
await tx.coins.put(coin); await tx.coins.put(coin);
const denoms = await getDenoms(coin.exchangeBaseUrl); const denoms = await getDenoms(coin.exchangeBaseUrl);
const cost = getTotalRefreshCost(denoms, denom, refreshAmount); const cost = getTotalRefreshCost(
denoms,
denom,
Amounts.parseOrThrow(refreshAmount),
);
const output = Amounts.sub(refreshAmount, cost).amount; const output = Amounts.sub(refreshAmount, cost).amount;
estimatedOutputPerCoin.push(output); estimatedOutputPerCoin.push(output);
} }
@ -934,13 +938,15 @@ export async function createRefreshGroup(
operationStatus: RefreshOperationStatus.Pending, operationStatus: RefreshOperationStatus.Pending,
timestampFinished: undefined, timestampFinished: undefined,
statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending), statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending),
lastErrorPerCoin: {},
oldCoinPubs: oldCoinPubs.map((x) => x.coinPub), oldCoinPubs: oldCoinPubs.map((x) => x.coinPub),
lastErrorPerCoin: {},
reason, reason,
refreshGroupId, refreshGroupId,
refreshSessionPerCoin: oldCoinPubs.map(() => undefined), refreshSessionPerCoin: oldCoinPubs.map(() => undefined),
inputPerCoin, inputPerCoin: inputPerCoin.map((x) => Amounts.stringify(x)),
estimatedOutputPerCoin, estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) =>
Amounts.stringify(x),
),
timestampCreated: TalerProtocolTimestamp.now(), timestampCreated: TalerProtocolTimestamp.now(),
}; };
@ -1037,11 +1043,11 @@ export async function autoRefresh(
if (AbsoluteTime.isExpired(executeThreshold)) { if (AbsoluteTime.isExpired(executeThreshold)) {
refreshCoins.push({ refreshCoins.push({
coinPub: coin.coinPub, coinPub: coin.coinPub,
amount: { amount: Amounts.stringify({
value: denom.amountVal, value: denom.amountVal,
fraction: denom.amountFrac, fraction: denom.amountFrac,
currency: denom.currency, currency: denom.currency,
}, }),
}); });
} else { } else {
const checkThreshold = getAutoRefreshCheckThreshold(denom); const checkThreshold = getAutoRefreshCheckThreshold(denom);

View File

@ -127,13 +127,13 @@ export async function prepareTip(
const newTipRecord: TipRecord = { const newTipRecord: TipRecord = {
walletTipId: walletTipId, walletTipId: walletTipId,
acceptedTimestamp: undefined, acceptedTimestamp: undefined,
tipAmountRaw: amount, tipAmountRaw: Amounts.stringify(amount),
tipExpiration: tipPickupStatus.expiration, tipExpiration: tipPickupStatus.expiration,
exchangeBaseUrl: tipPickupStatus.exchange_url, exchangeBaseUrl: tipPickupStatus.exchange_url,
merchantBaseUrl: res.merchantBaseUrl, merchantBaseUrl: res.merchantBaseUrl,
createdTimestamp: TalerProtocolTimestamp.now(), createdTimestamp: TalerProtocolTimestamp.now(),
merchantTipId: res.merchantTipId, merchantTipId: res.merchantTipId,
tipAmountEffective: selectedDenoms.totalCoinValue, tipAmountEffective: Amounts.stringify(selectedDenoms.totalCoinValue),
denomsSel: selectedDenoms, denomsSel: selectedDenoms,
pickedUpTimestamp: undefined, pickedUpTimestamp: undefined,
secretSeed, secretSeed,

View File

@ -26,6 +26,7 @@ import {
Logger, Logger,
OrderShortInfo, OrderShortInfo,
PaymentStatus, PaymentStatus,
PeerContractTerms,
RefundInfoShort, RefundInfoShort,
TalerProtocolTimestamp, TalerProtocolTimestamp,
Transaction, Transaction,
@ -49,6 +50,8 @@ import {
WithdrawalGroupRecord, WithdrawalGroupRecord,
WithdrawalRecordType, WithdrawalRecordType,
WalletContractData, WalletContractData,
PeerPushPaymentInitiationStatus,
PeerPullPaymentIncomingStatus,
} from "../db.js"; } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { assertUnreachable } from "../util/assertUnreachable.js"; import { assertUnreachable } from "../util/assertUnreachable.js";
@ -222,7 +225,7 @@ export async function getTransactionById(
const contractData = download.contractData; const contractData = download.contractData;
const refunds = mergeRefundByExecutionTime( const refunds = mergeRefundByExecutionTime(
cleanRefunds, cleanRefunds,
Amounts.getZero(contractData.amount.currency), Amounts.zeroOfAmount(contractData.amount),
); );
const payOpId = RetryTags.forPay(purchase); const payOpId = RetryTags.forPay(purchase);
@ -296,7 +299,7 @@ export async function getTransactionById(
const contractData = download.contractData; const contractData = download.contractData;
const refunds = mergeRefundByExecutionTime( const refunds = mergeRefundByExecutionTime(
[theRefund], [theRefund],
Amounts.getZero(contractData.amount.currency), Amounts.zeroOfAmount(contractData.amount),
); );
return buildTransactionForRefund( return buildTransactionForRefund(
@ -320,11 +323,13 @@ export async function getTransactionById(
} else if (type === TransactionType.PeerPushDebit) { } else if (type === TransactionType.PeerPushDebit) {
const pursePub = rest[0]; const pursePub = rest[0];
return await ws.db return await ws.db
.mktx((x) => [x.peerPushPaymentInitiations]) .mktx((x) => [x.peerPushPaymentInitiations, x.contractTerms])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const debit = await tx.peerPushPaymentInitiations.get(pursePub); const debit = await tx.peerPushPaymentInitiations.get(pursePub);
if (!debit) throw Error("not found"); if (!debit) throw Error("not found");
return buildTransactionForPushPaymentDebit(debit); const ct = await tx.contractTerms.get(debit.contractTermsHash);
checkDbInvariant(!!ct);
return buildTransactionForPushPaymentDebit(debit, ct.contractTermsRaw);
}); });
} else { } else {
const unknownTxType: never = type; const unknownTxType: never = type;
@ -334,6 +339,7 @@ export async function getTransactionById(
function buildTransactionForPushPaymentDebit( function buildTransactionForPushPaymentDebit(
pi: PeerPushPaymentInitiationRecord, pi: PeerPushPaymentInitiationRecord,
contractTerms: PeerContractTerms,
ort?: OperationRetryRecord, ort?: OperationRetryRecord,
): Transaction { ): Transaction {
return { return {
@ -342,11 +348,11 @@ function buildTransactionForPushPaymentDebit(
amountRaw: pi.amount, amountRaw: pi.amount,
exchangeBaseUrl: pi.exchangeBaseUrl, exchangeBaseUrl: pi.exchangeBaseUrl,
info: { info: {
expiration: pi.contractTerms.purse_expiration, expiration: contractTerms.purse_expiration,
summary: pi.contractTerms.summary, summary: contractTerms.summary,
}, },
frozen: false, frozen: false,
pending: !pi.purseCreated, pending: pi.status != PeerPushPaymentInitiationStatus.PurseCreated,
timestamp: pi.timestampCreated, timestamp: pi.timestampCreated,
talerUri: constructPayPushUri({ talerUri: constructPayPushUri({
exchangeBaseUrl: pi.exchangeBaseUrl, exchangeBaseUrl: pi.exchangeBaseUrl,
@ -586,7 +592,7 @@ function mergeRefundByExecutionTime(
prev.set(key, { prev.set(key, {
executionTime: refund.executionTime, executionTime: refund.executionTime,
amountAppliedEffective: effective, amountAppliedEffective: effective,
amountAppliedRaw: raw, amountAppliedRaw: Amounts.parseOrThrow(raw),
firstTimestamp: refund.obtainedTime, firstTimestamp: refund.obtainedTime,
}); });
} else { } else {
@ -659,7 +665,7 @@ async function buildTransactionForPurchase(
refundsInfo: MergedRefundInfo[], refundsInfo: MergedRefundInfo[],
ort?: OperationRetryRecord, ort?: OperationRetryRecord,
): Promise<Transaction> { ): Promise<Transaction> {
const zero = Amounts.getZero(contractData.amount.currency); const zero = Amounts.zeroOfAmount(contractData.amount);
const info: OrderShortInfo = { const info: OrderShortInfo = {
merchant: contractData.merchant, merchant: contractData.merchant,
@ -769,7 +775,11 @@ export async function getTransactions(
if (shouldSkipSearch(transactionsRequest, [])) { if (shouldSkipSearch(transactionsRequest, [])) {
return; return;
} }
transactions.push(buildTransactionForPushPaymentDebit(pi)); const ct = await tx.contractTerms.get(pi.contractTermsHash);
checkDbInvariant(!!ct);
transactions.push(
buildTransactionForPushPaymentDebit(pi, ct.contractTermsRaw),
);
}); });
tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => { tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => {
@ -780,7 +790,10 @@ export async function getTransactions(
if (shouldSkipSearch(transactionsRequest, [])) { if (shouldSkipSearch(transactionsRequest, [])) {
return; return;
} }
if (!pi.accepted) { if (
pi.status !== PeerPullPaymentIncomingStatus.Accepted &&
pi.status !== PeerPullPaymentIncomingStatus.Paid
) {
return; return;
} }
@ -791,7 +804,7 @@ export async function getTransactions(
if ( if (
shouldSkipCurrency( shouldSkipCurrency(
transactionsRequest, transactionsRequest,
wsr.rawWithdrawalAmount.currency, Amounts.currencyOf(wsr.rawWithdrawalAmount),
) )
) { ) {
return; return;
@ -899,7 +912,7 @@ export async function getTransactions(
const refunds = mergeRefundByExecutionTime( const refunds = mergeRefundByExecutionTime(
cleanRefunds, cleanRefunds,
Amounts.getZero(download.currency), Amounts.zeroOfCurrency(download.currency),
); );
refunds.forEach(async (refundInfo) => { refunds.forEach(async (refundInfo) => {
@ -929,7 +942,7 @@ export async function getTransactions(
if ( if (
shouldSkipCurrency( shouldSkipCurrency(
transactionsRequest, transactionsRequest,
tipRecord.tipAmountRaw.currency, Amounts.parseOrThrow(tipRecord.tipAmountRaw).currency,
) )
) { ) {
return; return;

View File

@ -39,26 +39,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -95,26 +95,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -150,26 +150,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -206,26 +206,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -261,26 +261,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,
@ -316,26 +316,26 @@ test("withdrawal selection bug repro", (t) => {
exchangeBaseUrl: "https://exchange.demo.taler.net/", exchangeBaseUrl: "https://exchange.demo.taler.net/",
exchangeMasterPub: "", exchangeMasterPub: "",
fees: { fees: {
feeDeposit: { feeDeposit: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefresh: { feeRefresh: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeRefund: { feeRefund: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
feeWithdraw: { feeWithdraw: Amounts.stringify({
currency: "KUDOS", currency: "KUDOS",
fraction: 1000000, fraction: 1000000,
value: 0, value: 0,
}, }),
}, },
isOffered: true, isOffered: true,
isRevoked: false, isRevoked: false,

View File

@ -167,8 +167,8 @@ export function selectWithdrawalDenominations(
denomPubHash: string; denomPubHash: string;
}[] = []; }[] = [];
let totalCoinValue = Amounts.getZero(amountAvailable.currency); let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency);
let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
denoms = denoms.filter(isWithdrawableDenom); denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => denoms.sort((d1, d2) =>
@ -223,8 +223,8 @@ export function selectWithdrawalDenominations(
return { return {
selectedDenoms, selectedDenoms,
totalCoinValue, totalCoinValue: Amounts.stringify(totalCoinValue),
totalWithdrawCost, totalWithdrawCost: Amounts.stringify(totalCoinValue),
}; };
} }
@ -238,8 +238,8 @@ export function selectForcedWithdrawalDenominations(
denomPubHash: string; denomPubHash: string;
}[] = []; }[] = [];
let totalCoinValue = Amounts.getZero(amountAvailable.currency); let totalCoinValue = Amounts.zeroOfCurrency(amountAvailable.currency);
let totalWithdrawCost = Amounts.getZero(amountAvailable.currency); let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
denoms = denoms.filter(isWithdrawableDenom); denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => denoms.sort((d1, d2) =>
@ -279,8 +279,8 @@ export function selectForcedWithdrawalDenominations(
return { return {
selectedDenoms, selectedDenoms,
totalCoinValue, totalCoinValue: Amounts.stringify(totalCoinValue),
totalWithdrawCost, totalWithdrawCost: Amounts.stringify(totalWithdrawCost),
}; };
} }
@ -416,10 +416,10 @@ async function processPlanchetGenerate(
checkDbInvariant(!!denom); checkDbInvariant(!!denom);
const r = await ws.cryptoApi.createPlanchet({ const r = await ws.cryptoApi.createPlanchet({
denomPub: denom.denomPub, denomPub: denom.denomPub,
feeWithdraw: denom.feeWithdraw, feeWithdraw: Amounts.parseOrThrow(denom.feeWithdraw),
reservePriv: withdrawalGroup.reservePriv, reservePriv: withdrawalGroup.reservePriv,
reservePub: withdrawalGroup.reservePub, reservePub: withdrawalGroup.reservePub,
value: denom.value, value: Amounts.parseOrThrow(denom.value),
coinIndex: coinIdx, coinIndex: coinIdx,
secretSeed: withdrawalGroup.secretSeed, secretSeed: withdrawalGroup.secretSeed,
restrictAge: withdrawalGroup.restrictAge, restrictAge: withdrawalGroup.restrictAge,
@ -950,7 +950,7 @@ async function queryReserve(
return; return;
} }
wg.status = WithdrawalGroupStatus.Ready; wg.status = WithdrawalGroupStatus.Ready;
wg.reserveBalanceAmount = Amounts.parse(result.response.balance); wg.reserveBalanceAmount = Amounts.stringify(result.response.balance);
await tx.withdrawalGroups.put(wg); await tx.withdrawalGroups.put(wg);
}); });
@ -1427,7 +1427,7 @@ export async function getFundingPaytoUrisTx(
export function augmentPaytoUrisForWithdrawal( export function augmentPaytoUrisForWithdrawal(
plainPaytoUris: string[], plainPaytoUris: string[],
reservePub: string, reservePub: string,
instructedAmount: AmountJson, instructedAmount: AmountLike,
): string[] { ): string[] {
return plainPaytoUris.map((x) => return plainPaytoUris.map((x) =>
addPaytoQueryParams(x, { addPaytoQueryParams(x, {
@ -1732,7 +1732,7 @@ export async function internalCreateWithdrawalGroup(
denomSelUid, denomSelUid,
denomsSel: initialDenomSel, denomsSel: initialDenomSel,
exchangeBaseUrl: canonExchange, exchangeBaseUrl: canonExchange,
instructedAmount: amount, instructedAmount: Amounts.stringify(amount),
timestampStart: now, timestampStart: now,
rawWithdrawalAmount: initialDenomSel.totalWithdrawCost, rawWithdrawalAmount: initialDenomSel.totalWithdrawCost,
effectiveWithdrawalAmount: initialDenomSel.totalCoinValue, effectiveWithdrawalAmount: initialDenomSel.totalCoinValue,

View File

@ -139,7 +139,7 @@ export function tallyFees(
if (!tally.wireFeeCoveredForExchange.has(exchangeBaseUrl)) { if (!tally.wireFeeCoveredForExchange.has(exchangeBaseUrl)) {
const wf = const wf =
wireFeesPerExchange[exchangeBaseUrl] ?? Amounts.getZero(currency); wireFeesPerExchange[exchangeBaseUrl] ?? Amounts.zeroOfCurrency(currency);
const wfForgiven = Amounts.min(amountWireFeeLimitRemaining, wf); const wfForgiven = Amounts.min(amountWireFeeLimitRemaining, wf);
amountWireFeeLimitRemaining = Amounts.sub( amountWireFeeLimitRemaining = Amounts.sub(
amountWireFeeLimitRemaining, amountWireFeeLimitRemaining,

View File

@ -25,6 +25,7 @@ import {
FeeDescriptionPair, FeeDescriptionPair,
Amounts, Amounts,
DenominationInfo, DenominationInfo,
AmountString,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
// import { expect } from "chai"; // import { expect } from "chai";
import { import {
@ -37,8 +38,8 @@ import test, { ExecutionContext } from "ava";
/** /**
* Create some constants to be used as reference in the tests * Create some constants to be used as reference in the tests
*/ */
const VALUES = Array.from({ length: 10 }).map((undef, t) => const VALUES: AmountString[] = Array.from({ length: 10 }).map(
Amounts.parseOrThrow(`USD:${t}`), (undef, t) => `USD:${t}`,
); );
const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s })); const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s }));
const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromTimestamp(m)); const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromTimestamp(m));

View File

@ -18,6 +18,7 @@ import {
AbsoluteTime, AbsoluteTime,
AmountJson, AmountJson,
Amounts, Amounts,
AmountString,
DenominationInfo, DenominationInfo,
FeeDescription, FeeDescription,
FeeDescriptionPair, FeeDescriptionPair,
@ -51,7 +52,7 @@ export function selectBestForOverlappingDenominations<
return minDeposit; return minDeposit;
} }
export function selectMinimumFee<T extends { fee: AmountJson }>( export function selectMinimumFee<T extends { fee: AmountString }>(
list: T[], list: T[],
): T | undefined { ): T | undefined {
let minFee: T | undefined = undefined; let minFee: T | undefined = undefined;
@ -285,7 +286,7 @@ export function createTimeline<Type extends object>(
idProp: PropsWithReturnType<Type, string>, idProp: PropsWithReturnType<Type, string>,
periodStartProp: PropsWithReturnType<Type, TalerProtocolTimestamp>, periodStartProp: PropsWithReturnType<Type, TalerProtocolTimestamp>,
periodEndProp: PropsWithReturnType<Type, TalerProtocolTimestamp>, periodEndProp: PropsWithReturnType<Type, TalerProtocolTimestamp>,
feeProp: PropsWithReturnType<Type, AmountJson>, feeProp: PropsWithReturnType<Type, AmountString>,
groupProp: PropsWithReturnType<Type, string> | undefined, groupProp: PropsWithReturnType<Type, string> | undefined,
selectBestForOverlapping: (l: Type[]) => Type | undefined, selectBestForOverlapping: (l: Type[]) => Type | undefined,
): FeeDescription[] { ): FeeDescription[] {
@ -312,7 +313,7 @@ export function createTimeline<Type extends object>(
} }
ps.push({ ps.push({
type: "start", type: "start",
fee, fee: Amounts.stringify(fee),
group, group,
id, id,
moment: AbsoluteTime.fromTimestamp(stampStart), moment: AbsoluteTime.fromTimestamp(stampStart),
@ -320,7 +321,7 @@ export function createTimeline<Type extends object>(
}); });
ps.push({ ps.push({
type: "end", type: "end",
fee, fee: Amounts.stringify(fee),
group, group,
id, id,
moment: AbsoluteTime.fromTimestamp(stampEnd), moment: AbsoluteTime.fromTimestamp(stampEnd),
@ -416,7 +417,7 @@ export function createTimeline<Type extends object>(
group: cursor.group, group: cursor.group,
from: cursor.moment, from: cursor.moment,
until: AbsoluteTime.never(), //not yet known until: AbsoluteTime.never(), //not yet known
fee: currentFee, fee: Amounts.stringify(currentFee),
}); });
} else { } else {
prev.until = cursor.moment; prev.until = cursor.moment;

View File

@ -771,7 +771,7 @@ async function getExchangeDetailedInfo(
const feesByGroup = [ const feesByGroup = [
...infoForType.map((w) => ({ ...infoForType.map((w) => ({
...w, ...w,
fee: w.closingFee, fee: Amounts.stringify(w.closingFee),
group: "closing", group: "closing",
})), })),
...infoForType.map((w) => ({ ...w, fee: w.wireFee, group: "wire" })), ...infoForType.map((w) => ({ ...w, fee: w.wireFee, group: "wire" })),

View File

@ -33,11 +33,7 @@ export default {
}; };
const cd: WalletContractData = { const cd: WalletContractData = {
amount: { amount: "ARS:2",
currency: "ARS",
fraction: 0,
value: 2,
},
contractTermsHash: contractTermsHash:
"92X0KSJPZ8XS2XECCGFWTCGW8XMFCXTT2S6WHZDP6H9Y3TSKMTHY94WXEWDERTNN5XWCYGW4VN5CF2D4846HXTW7P06J4CZMHCWKC9G", "92X0KSJPZ8XS2XECCGFWTCGW8XMFCXTT2S6WHZDP6H9Y3TSKMTHY94WXEWDERTNN5XWCYGW4VN5CF2D4846HXTW7P06J4CZMHCWKC9G",
fulfillmentUrl: "", fulfillmentUrl: "",
@ -47,11 +43,7 @@ const cd: WalletContractData = {
"0YA1WETV15R6K8QKS79QA3QMT16010F42Q49VSKYQ71HVQKAG0A4ZJCA4YTKHE9EA5SP156TJSKZEJJJ87305N6PS80PC48RNKYZE08", "0YA1WETV15R6K8QKS79QA3QMT16010F42Q49VSKYQ71HVQKAG0A4ZJCA4YTKHE9EA5SP156TJSKZEJJJ87305N6PS80PC48RNKYZE08",
orderId: "2022.220-0281XKKB8W7YE", orderId: "2022.220-0281XKKB8W7YE",
summary: "w", summary: "w",
maxWireFee: { maxWireFee: "ARS:1",
currency: "ARS",
fraction: 0,
value: 1,
},
payDeadline: { payDeadline: {
t_s: 1660002673, t_s: 1660002673,
}, },
@ -77,11 +69,7 @@ const cd: WalletContractData = {
wireMethod: "x-taler-bank", wireMethod: "x-taler-bank",
wireInfoHash: wireInfoHash:
"QDT28374ZHYJ59WQFZ3TW1D5WKJVDYHQT86VHED3TNMB15ANJSKXDYPPNX01348KDYCX6T4WXA5A8FJJ8YWNEB1JW726C1JPKHM89DR", "QDT28374ZHYJ59WQFZ3TW1D5WKJVDYHQT86VHED3TNMB15ANJSKXDYPPNX01348KDYCX6T4WXA5A8FJJ8YWNEB1JW726C1JPKHM89DR",
maxDepositFee: { maxDepositFee: "ARS:1",
currency: "ARS",
fraction: 0,
value: 1,
},
merchant: { merchant: {
name: "Default", name: "Default",
address: { address: {

View File

@ -99,7 +99,7 @@ export function useComponentState(
const balance = const balance =
bs.length > 0 bs.length > 0
? Amounts.parseOrThrow(bs[0].available) ? Amounts.parseOrThrow(bs[0].available)
: Amounts.getZero(currency); : Amounts.zeroOfCurrency(currency);
if (Amounts.isZero(balance)) { if (Amounts.isZero(balance)) {
return { return {
@ -157,12 +157,12 @@ export function useComponentState(
const totalFee = const totalFee =
fee !== undefined fee !== undefined
? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount ? Amounts.sum([fee.wire, fee.coin, fee.refresh]).amount
: Amounts.getZero(currency); : Amounts.zeroOfCurrency(currency);
const totalToDeposit = const totalToDeposit =
parsedAmount && fee !== undefined parsedAmount && fee !== undefined
? Amounts.sub(parsedAmount, totalFee).amount ? Amounts.sub(parsedAmount, totalFee).amount
: Amounts.getZero(currency); : Amounts.zeroOfCurrency(currency);
const isDirty = amount !== initialValue; const isDirty = amount !== initialValue;
const amountError = !isDirty const amountError = !isDirty

View File

@ -76,7 +76,7 @@ export const WithNoAccountForIBAN = createExample(ReadyView, {
return; return;
}, },
}, },
totalFee: Amounts.getZero("USD"), totalFee: Amounts.zeroOfCurrency("USD"),
totalToDeposit: Amounts.parseOrThrow("USD:10"), totalToDeposit: Amounts.parseOrThrow("USD:10"),
// onCalculateFee: alwaysReturnFeeToOne, // onCalculateFee: alwaysReturnFeeToOne,
}); });
@ -111,7 +111,7 @@ export const WithIBANAccountTypeSelected = createExample(ReadyView, {
return; return;
}, },
}, },
totalFee: Amounts.getZero("USD"), totalFee: Amounts.zeroOfCurrency("USD"),
totalToDeposit: Amounts.parseOrThrow("USD:10"), totalToDeposit: Amounts.parseOrThrow("USD:10"),
// onCalculateFee: alwaysReturnFeeToOne, // onCalculateFee: alwaysReturnFeeToOne,
}); });
@ -146,7 +146,7 @@ export const NewBitcoinAccountTypeSelected = createExample(ReadyView, {
return; return;
}, },
}, },
totalFee: Amounts.getZero("USD"), totalFee: Amounts.zeroOfCurrency("USD"),
totalToDeposit: Amounts.parseOrThrow("USD:10"), totalToDeposit: Amounts.parseOrThrow("USD:10"),
// onCalculateFee: alwaysReturnFeeToOne, // onCalculateFee: alwaysReturnFeeToOne,
}); });

View File

@ -1132,7 +1132,7 @@ export function PurchaseDetails({
const partialFee = Amounts.sub(price.effective, price.raw).amount; const partialFee = Amounts.sub(price.effective, price.raw).amount;
const refundFee = !refund const refundFee = !refund
? Amounts.getZero(price.effective.currency) ? Amounts.zeroOfCurrency(price.effective.currency)
: Amounts.sub(refund.raw, refund.effective).amount; : Amounts.sub(refund.raw, refund.effective).amount;
const fee = Amounts.sum([partialFee, refundFee]).amount; const fee = Amounts.sum([partialFee, refundFee]).amount;