wallet-core: store total p2p push cost in DB
This commit is contained in:
parent
a3f9e86805
commit
a31b8c3c31
@ -1713,6 +1713,8 @@ export interface PeerPushPaymentInitiationRecord {
|
|||||||
*/
|
*/
|
||||||
amount: AmountString;
|
amount: AmountString;
|
||||||
|
|
||||||
|
totalCost: AmountString;
|
||||||
|
|
||||||
coinSel: PeerPushPaymentCoinSelection;
|
coinSel: PeerPushPaymentCoinSelection;
|
||||||
|
|
||||||
contractTermsHash: HashCodeString;
|
contractTermsHash: HashCodeString;
|
||||||
|
@ -103,20 +103,22 @@ import { internalCreateWithdrawalGroup } from "./withdraw.js";
|
|||||||
|
|
||||||
const logger = new Logger("operations/peer-to-peer.ts");
|
const logger = new Logger("operations/peer-to-peer.ts");
|
||||||
|
|
||||||
export interface PeerCoinSelectionDetails {
|
interface SelectedPeerCoin {
|
||||||
|
coinPub: string;
|
||||||
|
coinPriv: string;
|
||||||
|
contribution: AmountString;
|
||||||
|
denomPubHash: string;
|
||||||
|
denomSig: UnblindedSignature;
|
||||||
|
ageCommitmentProof: AgeCommitmentProof | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PeerCoinSelectionDetails {
|
||||||
exchangeBaseUrl: string;
|
exchangeBaseUrl: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Info of Coins that were selected.
|
* Info of Coins that were selected.
|
||||||
*/
|
*/
|
||||||
coins: {
|
coins: SelectedPeerCoin[];
|
||||||
coinPub: string;
|
|
||||||
coinPriv: string;
|
|
||||||
contribution: AmountString;
|
|
||||||
denomPubHash: string;
|
|
||||||
denomSig: UnblindedSignature;
|
|
||||||
ageCommitmentProof: AgeCommitmentProof | undefined;
|
|
||||||
}[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How much of the deposit fees is the customer paying?
|
* How much of the deposit fees is the customer paying?
|
||||||
@ -195,152 +197,158 @@ export async function queryCoinInfosForSelection(
|
|||||||
|
|
||||||
export async function selectPeerCoins(
|
export async function selectPeerCoins(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
tx: GetReadOnlyAccess<{
|
|
||||||
exchanges: typeof WalletStoresV1.exchanges;
|
|
||||||
denominations: typeof WalletStoresV1.denominations;
|
|
||||||
coins: typeof WalletStoresV1.coins;
|
|
||||||
coinAvailability: typeof WalletStoresV1.coinAvailability;
|
|
||||||
refreshGroups: typeof WalletStoresV1.refreshGroups;
|
|
||||||
}>,
|
|
||||||
instructedAmount: AmountJson,
|
instructedAmount: AmountJson,
|
||||||
): Promise<SelectPeerCoinsResult> {
|
): Promise<SelectPeerCoinsResult> {
|
||||||
const exchanges = await tx.exchanges.iter().toArray();
|
return await ws.db
|
||||||
const exchangeFeeGap: { [url: string]: AmountJson } = {};
|
.mktx((x) => [
|
||||||
const currency = Amounts.currencyOf(instructedAmount);
|
x.exchanges,
|
||||||
for (const exch of exchanges) {
|
x.contractTerms,
|
||||||
if (exch.detailsPointer?.currency !== currency) {
|
x.coins,
|
||||||
continue;
|
x.coinAvailability,
|
||||||
}
|
x.denominations,
|
||||||
const coins = (
|
x.refreshGroups,
|
||||||
await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
|
x.peerPushPaymentInitiations,
|
||||||
).filter((x) => x.status === CoinStatus.Fresh);
|
])
|
||||||
const coinInfos: CoinInfo[] = [];
|
.runReadWrite(async (tx) => {
|
||||||
for (const coin of coins) {
|
const exchanges = await tx.exchanges.iter().toArray();
|
||||||
const denom = await ws.getDenomInfo(
|
const exchangeFeeGap: { [url: string]: AmountJson } = {};
|
||||||
ws,
|
const currency = Amounts.currencyOf(instructedAmount);
|
||||||
tx,
|
for (const exch of exchanges) {
|
||||||
coin.exchangeBaseUrl,
|
if (exch.detailsPointer?.currency !== currency) {
|
||||||
coin.denomPubHash,
|
continue;
|
||||||
);
|
}
|
||||||
if (!denom) {
|
const coins = (
|
||||||
throw Error("denom not found");
|
await tx.coins.indexes.byBaseUrl.getAll(exch.baseUrl)
|
||||||
|
).filter((x) => x.status === CoinStatus.Fresh);
|
||||||
|
const coinInfos: CoinInfo[] = [];
|
||||||
|
for (const coin of coins) {
|
||||||
|
const denom = await ws.getDenomInfo(
|
||||||
|
ws,
|
||||||
|
tx,
|
||||||
|
coin.exchangeBaseUrl,
|
||||||
|
coin.denomPubHash,
|
||||||
|
);
|
||||||
|
if (!denom) {
|
||||||
|
throw Error("denom not found");
|
||||||
|
}
|
||||||
|
coinInfos.push({
|
||||||
|
coinPub: coin.coinPub,
|
||||||
|
feeDeposit: Amounts.parseOrThrow(denom.feeDeposit),
|
||||||
|
value: Amounts.parseOrThrow(denom.value),
|
||||||
|
denomPubHash: denom.denomPubHash,
|
||||||
|
coinPriv: coin.coinPriv,
|
||||||
|
denomSig: coin.denomSig,
|
||||||
|
maxAge: coin.maxAge,
|
||||||
|
ageCommitmentProof: coin.ageCommitmentProof,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (coinInfos.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
coinInfos.sort(
|
||||||
|
(o1, o2) =>
|
||||||
|
-Amounts.cmp(o1.value, o2.value) ||
|
||||||
|
strcmp(o1.denomPubHash, o2.denomPubHash),
|
||||||
|
);
|
||||||
|
let amountAcc = Amounts.zeroOfCurrency(currency);
|
||||||
|
let depositFeesAcc = Amounts.zeroOfCurrency(currency);
|
||||||
|
const resCoins: {
|
||||||
|
coinPub: string;
|
||||||
|
coinPriv: string;
|
||||||
|
contribution: AmountString;
|
||||||
|
denomPubHash: string;
|
||||||
|
denomSig: UnblindedSignature;
|
||||||
|
ageCommitmentProof: AgeCommitmentProof | undefined;
|
||||||
|
}[] = [];
|
||||||
|
let lastDepositFee = Amounts.zeroOfCurrency(currency);
|
||||||
|
for (const coin of coinInfos) {
|
||||||
|
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const gap = Amounts.add(
|
||||||
|
coin.feeDeposit,
|
||||||
|
Amounts.sub(instructedAmount, amountAcc).amount,
|
||||||
|
).amount;
|
||||||
|
const contrib = Amounts.min(gap, coin.value);
|
||||||
|
amountAcc = Amounts.add(
|
||||||
|
amountAcc,
|
||||||
|
Amounts.sub(contrib, coin.feeDeposit).amount,
|
||||||
|
).amount;
|
||||||
|
depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
|
||||||
|
resCoins.push({
|
||||||
|
coinPriv: coin.coinPriv,
|
||||||
|
coinPub: coin.coinPub,
|
||||||
|
contribution: Amounts.stringify(contrib),
|
||||||
|
denomPubHash: coin.denomPubHash,
|
||||||
|
denomSig: coin.denomSig,
|
||||||
|
ageCommitmentProof: coin.ageCommitmentProof,
|
||||||
|
});
|
||||||
|
lastDepositFee = coin.feeDeposit;
|
||||||
|
}
|
||||||
|
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
||||||
|
const res: PeerCoinSelectionDetails = {
|
||||||
|
exchangeBaseUrl: exch.baseUrl,
|
||||||
|
coins: resCoins,
|
||||||
|
depositFees: depositFeesAcc,
|
||||||
|
};
|
||||||
|
return { type: "success", result: res };
|
||||||
|
}
|
||||||
|
const diff = Amounts.sub(instructedAmount, amountAcc).amount;
|
||||||
|
exchangeFeeGap[exch.baseUrl] = Amounts.add(lastDepositFee, diff).amount;
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
coinInfos.push({
|
// We were unable to select coins.
|
||||||
coinPub: coin.coinPub,
|
// Now we need to produce error details.
|
||||||
feeDeposit: Amounts.parseOrThrow(denom.feeDeposit),
|
|
||||||
value: Amounts.parseOrThrow(denom.value),
|
const infoGeneral = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
|
||||||
denomPubHash: denom.denomPubHash,
|
currency,
|
||||||
coinPriv: coin.coinPriv,
|
|
||||||
denomSig: coin.denomSig,
|
|
||||||
maxAge: coin.maxAge,
|
|
||||||
ageCommitmentProof: coin.ageCommitmentProof,
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
if (coinInfos.length === 0) {
|
const perExchange: PayPeerInsufficientBalanceDetails["perExchange"] = {};
|
||||||
continue;
|
|
||||||
}
|
for (const exch of exchanges) {
|
||||||
coinInfos.sort(
|
if (exch.detailsPointer?.currency !== currency) {
|
||||||
(o1, o2) =>
|
continue;
|
||||||
-Amounts.cmp(o1.value, o2.value) ||
|
}
|
||||||
strcmp(o1.denomPubHash, o2.denomPubHash),
|
const infoExchange = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
|
||||||
);
|
currency,
|
||||||
let amountAcc = Amounts.zeroOfCurrency(currency);
|
restrictExchangeTo: exch.baseUrl,
|
||||||
let depositFeesAcc = Amounts.zeroOfCurrency(currency);
|
});
|
||||||
const resCoins: {
|
let gap =
|
||||||
coinPub: string;
|
exchangeFeeGap[exch.baseUrl] ?? Amounts.zeroOfCurrency(currency);
|
||||||
coinPriv: string;
|
if (Amounts.cmp(infoExchange.balanceMaterial, instructedAmount) < 0) {
|
||||||
contribution: AmountString;
|
// Show fee gap only if we should've been able to pay with the material amount
|
||||||
denomPubHash: string;
|
gap = Amounts.zeroOfAmount(currency);
|
||||||
denomSig: UnblindedSignature;
|
}
|
||||||
ageCommitmentProof: AgeCommitmentProof | undefined;
|
perExchange[exch.baseUrl] = {
|
||||||
}[] = [];
|
balanceAvailable: Amounts.stringify(infoExchange.balanceAvailable),
|
||||||
let lastDepositFee = Amounts.zeroOfCurrency(currency);
|
balanceMaterial: Amounts.stringify(infoExchange.balanceMaterial),
|
||||||
for (const coin of coinInfos) {
|
feeGapEstimate: Amounts.stringify(gap),
|
||||||
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
};
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
const gap = Amounts.add(
|
|
||||||
coin.feeDeposit,
|
const errDetails: PayPeerInsufficientBalanceDetails = {
|
||||||
Amounts.sub(instructedAmount, amountAcc).amount,
|
amountRequested: Amounts.stringify(instructedAmount),
|
||||||
).amount;
|
balanceAvailable: Amounts.stringify(infoGeneral.balanceAvailable),
|
||||||
const contrib = Amounts.min(gap, coin.value);
|
balanceMaterial: Amounts.stringify(infoGeneral.balanceMaterial),
|
||||||
amountAcc = Amounts.add(
|
perExchange,
|
||||||
amountAcc,
|
|
||||||
Amounts.sub(contrib, coin.feeDeposit).amount,
|
|
||||||
).amount;
|
|
||||||
depositFeesAcc = Amounts.add(depositFeesAcc, coin.feeDeposit).amount;
|
|
||||||
resCoins.push({
|
|
||||||
coinPriv: coin.coinPriv,
|
|
||||||
coinPub: coin.coinPub,
|
|
||||||
contribution: Amounts.stringify(contrib),
|
|
||||||
denomPubHash: coin.denomPubHash,
|
|
||||||
denomSig: coin.denomSig,
|
|
||||||
ageCommitmentProof: coin.ageCommitmentProof,
|
|
||||||
});
|
|
||||||
lastDepositFee = coin.feeDeposit;
|
|
||||||
}
|
|
||||||
if (Amounts.cmp(amountAcc, instructedAmount) >= 0) {
|
|
||||||
const res: PeerCoinSelectionDetails = {
|
|
||||||
exchangeBaseUrl: exch.baseUrl,
|
|
||||||
coins: resCoins,
|
|
||||||
depositFees: depositFeesAcc,
|
|
||||||
};
|
};
|
||||||
return { type: "success", result: res };
|
|
||||||
}
|
|
||||||
const diff = Amounts.sub(instructedAmount, amountAcc).amount;
|
|
||||||
exchangeFeeGap[exch.baseUrl] = Amounts.add(lastDepositFee, diff).amount;
|
|
||||||
|
|
||||||
continue;
|
return { type: "failure", insufficientBalanceDetails: errDetails };
|
||||||
}
|
|
||||||
// We were unable to select coins.
|
|
||||||
// Now we need to produce error details.
|
|
||||||
|
|
||||||
const infoGeneral = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
|
|
||||||
currency,
|
|
||||||
});
|
|
||||||
|
|
||||||
const perExchange: PayPeerInsufficientBalanceDetails["perExchange"] = {};
|
|
||||||
|
|
||||||
for (const exch of exchanges) {
|
|
||||||
if (exch.detailsPointer?.currency !== currency) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const infoExchange = await getPeerPaymentBalanceDetailsInTx(ws, tx, {
|
|
||||||
currency,
|
|
||||||
restrictExchangeTo: exch.baseUrl,
|
|
||||||
});
|
});
|
||||||
let gap = exchangeFeeGap[exch.baseUrl] ?? Amounts.zeroOfCurrency(currency);
|
|
||||||
if (Amounts.cmp(infoExchange.balanceMaterial, instructedAmount) < 0) {
|
|
||||||
// Show fee gap only if we should've been able to pay with the material amount
|
|
||||||
gap = Amounts.zeroOfAmount(currency);
|
|
||||||
}
|
|
||||||
perExchange[exch.baseUrl] = {
|
|
||||||
balanceAvailable: Amounts.stringify(infoExchange.balanceAvailable),
|
|
||||||
balanceMaterial: Amounts.stringify(infoExchange.balanceMaterial),
|
|
||||||
feeGapEstimate: Amounts.stringify(gap),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const errDetails: PayPeerInsufficientBalanceDetails = {
|
|
||||||
amountRequested: Amounts.stringify(instructedAmount),
|
|
||||||
balanceAvailable: Amounts.stringify(infoGeneral.balanceAvailable),
|
|
||||||
balanceMaterial: Amounts.stringify(infoGeneral.balanceMaterial),
|
|
||||||
perExchange,
|
|
||||||
};
|
|
||||||
|
|
||||||
return { type: "failure", insufficientBalanceDetails: errDetails };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTotalPeerPaymentCost(
|
export async function getTotalPeerPaymentCost(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
pcs: PeerCoinSelectionDetails,
|
pcs: SelectedPeerCoin[],
|
||||||
): Promise<AmountJson> {
|
): Promise<AmountJson> {
|
||||||
return ws.db
|
return ws.db
|
||||||
.mktx((x) => [x.coins, x.denominations])
|
.mktx((x) => [x.coins, x.denominations])
|
||||||
.runReadOnly(async (tx) => {
|
.runReadOnly(async (tx) => {
|
||||||
const costs: AmountJson[] = [];
|
const costs: AmountJson[] = [];
|
||||||
for (let i = 0; i < pcs.coins.length; i++) {
|
for (let i = 0; i < pcs.length; i++) {
|
||||||
const coin = await tx.coins.get(pcs.coins[i].coinPub);
|
const coin = await tx.coins.get(pcs[i].coinPub);
|
||||||
if (!coin) {
|
if (!coin) {
|
||||||
throw Error("can't calculate payment cost, coin not found");
|
throw Error("can't calculate payment cost, coin not found");
|
||||||
}
|
}
|
||||||
@ -358,22 +366,22 @@ export async function getTotalPeerPaymentCost(
|
|||||||
.filter((x) =>
|
.filter((x) =>
|
||||||
Amounts.isSameCurrency(
|
Amounts.isSameCurrency(
|
||||||
DenominationRecord.getValue(x),
|
DenominationRecord.getValue(x),
|
||||||
pcs.coins[i].contribution,
|
pcs[i].contribution,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const amountLeft = Amounts.sub(
|
const amountLeft = Amounts.sub(
|
||||||
DenominationRecord.getValue(denom),
|
DenominationRecord.getValue(denom),
|
||||||
pcs.coins[i].contribution,
|
pcs[i].contribution,
|
||||||
).amount;
|
).amount;
|
||||||
const refreshCost = getTotalRefreshCost(
|
const refreshCost = getTotalRefreshCost(
|
||||||
allDenoms,
|
allDenoms,
|
||||||
DenominationRecord.toDenomInfo(denom),
|
DenominationRecord.toDenomInfo(denom),
|
||||||
amountLeft,
|
amountLeft,
|
||||||
);
|
);
|
||||||
costs.push(Amounts.parseOrThrow(pcs.coins[i].contribution));
|
costs.push(Amounts.parseOrThrow(pcs[i].contribution));
|
||||||
costs.push(refreshCost);
|
costs.push(refreshCost);
|
||||||
}
|
}
|
||||||
const zero = Amounts.zeroOfAmount(pcs.coins[0].contribution);
|
const zero = Amounts.zeroOfAmount(pcs[0].contribution);
|
||||||
return Amounts.sum([zero, ...costs]).amount;
|
return Amounts.sum([zero, ...costs]).amount;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -383,20 +391,7 @@ export async function preparePeerPushPayment(
|
|||||||
req: PreparePeerPushPaymentRequest,
|
req: PreparePeerPushPaymentRequest,
|
||||||
): Promise<PreparePeerPushPaymentResponse> {
|
): Promise<PreparePeerPushPaymentResponse> {
|
||||||
const instructedAmount = Amounts.parseOrThrow(req.amount);
|
const instructedAmount = Amounts.parseOrThrow(req.amount);
|
||||||
const coinSelRes: SelectPeerCoinsResult = await ws.db
|
const coinSelRes = await selectPeerCoins(ws, instructedAmount);
|
||||||
.mktx((x) => [
|
|
||||||
x.exchanges,
|
|
||||||
x.contractTerms,
|
|
||||||
x.coins,
|
|
||||||
x.coinAvailability,
|
|
||||||
x.denominations,
|
|
||||||
x.refreshGroups,
|
|
||||||
x.peerPushPaymentInitiations,
|
|
||||||
])
|
|
||||||
.runReadWrite(async (tx) => {
|
|
||||||
const selRes = await selectPeerCoins(ws, tx, instructedAmount);
|
|
||||||
return selRes;
|
|
||||||
});
|
|
||||||
if (coinSelRes.type === "failure") {
|
if (coinSelRes.type === "failure") {
|
||||||
throw TalerError.fromDetail(
|
throw TalerError.fromDetail(
|
||||||
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
||||||
@ -405,7 +400,10 @@ export async function preparePeerPushPayment(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const totalAmount = await getTotalPeerPaymentCost(ws, coinSelRes.result);
|
const totalAmount = await getTotalPeerPaymentCost(
|
||||||
|
ws,
|
||||||
|
coinSelRes.result.coins,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
amountEffective: Amounts.stringify(totalAmount),
|
amountEffective: Amounts.stringify(totalAmount),
|
||||||
amountRaw: req.amount,
|
amountRaw: req.amount,
|
||||||
@ -517,7 +515,28 @@ export async function initiatePeerPushPayment(
|
|||||||
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
const hContractTerms = ContractTermsUtil.hashContractTerms(contractTerms);
|
||||||
|
|
||||||
const contractKeyPair = await ws.cryptoApi.createEddsaKeypair({});
|
const contractKeyPair = await ws.cryptoApi.createEddsaKeypair({});
|
||||||
const coinSelRes: SelectPeerCoinsResult = await ws.db
|
|
||||||
|
const coinSelRes = await selectPeerCoins(ws, instructedAmount);
|
||||||
|
|
||||||
|
if (coinSelRes.type !== "success") {
|
||||||
|
throw TalerError.fromDetail(
|
||||||
|
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
||||||
|
{
|
||||||
|
insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sel = coinSelRes.result;
|
||||||
|
|
||||||
|
logger.info(`selected p2p coins (push): ${j2s(coinSelRes)}`);
|
||||||
|
|
||||||
|
const totalAmount = await getTotalPeerPaymentCost(
|
||||||
|
ws,
|
||||||
|
coinSelRes.result.coins,
|
||||||
|
);
|
||||||
|
|
||||||
|
await ws.db
|
||||||
.mktx((x) => [
|
.mktx((x) => [
|
||||||
x.exchanges,
|
x.exchanges,
|
||||||
x.contractTerms,
|
x.contractTerms,
|
||||||
@ -528,13 +547,6 @@ export async function initiatePeerPushPayment(
|
|||||||
x.peerPushPaymentInitiations,
|
x.peerPushPaymentInitiations,
|
||||||
])
|
])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const selRes = await selectPeerCoins(ws, tx, instructedAmount);
|
|
||||||
if (selRes.type === "failure") {
|
|
||||||
return selRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sel = selRes.result;
|
|
||||||
|
|
||||||
await spendCoins(ws, tx, {
|
await spendCoins(ws, tx, {
|
||||||
allocationId: `txn:peer-push-debit:${pursePair.pub}`,
|
allocationId: `txn:peer-push-debit:${pursePair.pub}`,
|
||||||
coinPubs: sel.coins.map((x) => x.coinPub),
|
coinPubs: sel.coins.map((x) => x.coinPub),
|
||||||
@ -562,25 +574,14 @@ export async function initiatePeerPushPayment(
|
|||||||
coinPubs: sel.coins.map((x) => x.coinPub),
|
coinPubs: sel.coins.map((x) => x.coinPub),
|
||||||
contributions: sel.coins.map((x) => x.contribution),
|
contributions: sel.coins.map((x) => x.contribution),
|
||||||
},
|
},
|
||||||
|
totalCost: Amounts.stringify(totalAmount),
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.contractTerms.put({
|
await tx.contractTerms.put({
|
||||||
h: hContractTerms,
|
h: hContractTerms,
|
||||||
contractTermsRaw: contractTerms,
|
contractTermsRaw: contractTerms,
|
||||||
});
|
});
|
||||||
|
|
||||||
return selRes;
|
|
||||||
});
|
});
|
||||||
logger.info(`selected p2p coins (push): ${j2s(coinSelRes)}`);
|
|
||||||
|
|
||||||
if (coinSelRes.type !== "success") {
|
|
||||||
throw TalerError.fromDetail(
|
|
||||||
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
|
||||||
{
|
|
||||||
insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await runOperationWithErrorReporting(
|
await runOperationWithErrorReporting(
|
||||||
ws,
|
ws,
|
||||||
@ -866,7 +867,22 @@ export async function acceptPeerPullPayment(
|
|||||||
const instructedAmount = Amounts.parseOrThrow(
|
const instructedAmount = Amounts.parseOrThrow(
|
||||||
peerPullInc.contractTerms.amount,
|
peerPullInc.contractTerms.amount,
|
||||||
);
|
);
|
||||||
const coinSelRes: SelectPeerCoinsResult = await ws.db
|
|
||||||
|
const coinSelRes = await selectPeerCoins(ws, instructedAmount);
|
||||||
|
logger.info(`selected p2p coins (pull): ${j2s(coinSelRes)}`);
|
||||||
|
|
||||||
|
if (coinSelRes.type !== "success") {
|
||||||
|
throw TalerError.fromDetail(
|
||||||
|
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
||||||
|
{
|
||||||
|
insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sel = coinSelRes.result;
|
||||||
|
|
||||||
|
await ws.db
|
||||||
.mktx((x) => [
|
.mktx((x) => [
|
||||||
x.exchanges,
|
x.exchanges,
|
||||||
x.coins,
|
x.coins,
|
||||||
@ -876,13 +892,6 @@ export async function acceptPeerPullPayment(
|
|||||||
x.coinAvailability,
|
x.coinAvailability,
|
||||||
])
|
])
|
||||||
.runReadWrite(async (tx) => {
|
.runReadWrite(async (tx) => {
|
||||||
const selRes = await selectPeerCoins(ws, tx, instructedAmount);
|
|
||||||
if (selRes.type !== "success") {
|
|
||||||
return selRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sel = selRes.result;
|
|
||||||
|
|
||||||
await spendCoins(ws, tx, {
|
await spendCoins(ws, tx, {
|
||||||
allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`,
|
allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`,
|
||||||
coinPubs: sel.coins.map((x) => x.coinPub),
|
coinPubs: sel.coins.map((x) => x.coinPub),
|
||||||
@ -900,19 +909,7 @@ export async function acceptPeerPullPayment(
|
|||||||
}
|
}
|
||||||
pi.status = PeerPullPaymentIncomingStatus.Accepted;
|
pi.status = PeerPullPaymentIncomingStatus.Accepted;
|
||||||
await tx.peerPullPaymentIncoming.put(pi);
|
await tx.peerPullPaymentIncoming.put(pi);
|
||||||
|
|
||||||
return selRes;
|
|
||||||
});
|
});
|
||||||
logger.info(`selected p2p coins (pull): ${j2s(coinSelRes)}`);
|
|
||||||
|
|
||||||
if (coinSelRes.type !== "success") {
|
|
||||||
throw TalerError.fromDetail(
|
|
||||||
TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE,
|
|
||||||
{
|
|
||||||
insufficientBalanceDetails: coinSelRes.insufficientBalanceDetails,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pursePub = peerPullInc.pursePub;
|
const pursePub = peerPullInc.pursePub;
|
||||||
|
|
||||||
|
@ -346,7 +346,7 @@ function buildTransactionForPushPaymentDebit(
|
|||||||
): Transaction {
|
): Transaction {
|
||||||
return {
|
return {
|
||||||
type: TransactionType.PeerPushDebit,
|
type: TransactionType.PeerPushDebit,
|
||||||
amountEffective: pi.amount,
|
amountEffective: pi.totalCost,
|
||||||
amountRaw: pi.amount,
|
amountRaw: pi.amount,
|
||||||
exchangeBaseUrl: pi.exchangeBaseUrl,
|
exchangeBaseUrl: pi.exchangeBaseUrl,
|
||||||
info: {
|
info: {
|
||||||
|
Loading…
Reference in New Issue
Block a user