Skip to content

Commit 35db9ec

Browse files
authored
fix: correct open issues with settlementDisplayAmount gql selection (#2393)
* test: expect correct decimal places for settlementDisplayAmount * fix: change settlementDisplayAmount type to string * test: add settlementDisplayAmount checks to onchain-send * feat: add new SettlementAmounts calculator * test: add unit tests for SettlementAmounts * fix: switch to SettlementAmounts calculator in tx-history * test: add settlementDisplayAmount check for pending incoming onchain txn * fix: correct price for calculating pending incoming settlementDisplayAmount
1 parent 07ea3cb commit 35db9ec

File tree

9 files changed

+349
-20
lines changed

9 files changed

+349
-20
lines changed

src/domain/fiat/display-currency.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ export const minorToMajorUnit = ({
1414
displayMajorExponent: CurrencyMajorExponent
1515
}) => {
1616
const majorExponent = Number(displayMajorExponent)
17-
return Number((Number(amount) / 10 ** majorExponent).toFixed(majorExponent))
17+
return (Number(amount) / 10 ** majorExponent).toFixed(majorExponent)
1818
}
1919

2020
export const usdMinorToMajorUnit = (amount: number | bigint) =>
21-
minorToMajorUnit({ amount, displayMajorExponent: MajorExponent.STANDARD })
21+
Number(minorToMajorUnit({ amount, displayMajorExponent: MajorExponent.STANDARD }))
2222

2323
export const majorToMinorUnit = ({
2424
amount,

src/domain/wallets/index.types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ type BaseWalletTransaction = {
5252
readonly settlementAmount: Satoshis | UsdCents
5353
readonly settlementFee: Satoshis | UsdCents
5454
readonly settlementCurrency: WalletCurrency
55-
readonly settlementDisplayAmount: number
55+
readonly settlementDisplayAmount: DisplayCurrencyMajorAmount
5656
readonly settlementDisplayCurrency: DisplayCurrency | ""
5757
readonly displayCurrencyPerSettlementCurrencyUnit: number
5858
readonly status: TxStatus
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { toSats } from "@domain/bitcoin"
2+
import { MajorExponent, minorToMajorUnit, toCents } from "@domain/fiat"
3+
import { WalletCurrency } from "@domain/shared"
4+
5+
export const SettlementAmounts = () => {
6+
const fromTxn = <S extends WalletCurrency>(
7+
txn: LedgerTransaction<S>,
8+
): {
9+
settlementAmount: Satoshis | UsdCents
10+
settlementDisplayAmount: DisplayCurrencyMajorAmount
11+
} => {
12+
// ======
13+
// Calculate: settlementAmount
14+
// ======
15+
16+
const { debit, credit, currency } = txn
17+
const settlementAmount =
18+
currency === WalletCurrency.Btc ? toSats(credit - debit) : toCents(credit - debit)
19+
20+
// ======
21+
// Calculate: settlementDisplayAmount
22+
// ======
23+
24+
// Setup settlementDisplayAmount calc
25+
const {
26+
satsAmount: satsAmountRaw,
27+
satsFee: satsFeeRaw,
28+
centsAmount: centsAmountRaw,
29+
centsFee: centsFeeRaw,
30+
displayAmount: displayAmountRaw,
31+
displayFee: displayFeeRaw,
32+
} = txn
33+
34+
const satsAmount = satsAmountRaw || toSats(0)
35+
const satsFee = satsFeeRaw || toSats(0)
36+
const centsAmount = centsAmountRaw || toCents(0)
37+
const centsFee = centsFeeRaw || toCents(0)
38+
const displayAmount = displayAmountRaw || (0 as DisplayCurrencyBaseAmount)
39+
const displayFee = displayFeeRaw || (0 as DisplayCurrencyBaseAmount)
40+
41+
const amount = {
42+
sats: satsAmount,
43+
cents: centsAmount,
44+
display: displayAmount,
45+
}
46+
const negAmount = {
47+
sats: toSats(-satsAmount),
48+
cents: toCents(-centsAmount),
49+
display: -displayAmount as DisplayCurrencyBaseAmount,
50+
}
51+
const fee = {
52+
sats: satsFee,
53+
cents: centsFee,
54+
display: displayFee,
55+
}
56+
const negFee = {
57+
sats: toSats(-satsFee),
58+
cents: toCents(-centsFee),
59+
display: -displayFee as DisplayCurrencyBaseAmount,
60+
}
61+
const zero = {
62+
sats: 0 as Satoshis,
63+
cents: 0 as UsdCents,
64+
display: 0 as DisplayCurrencyBaseAmount,
65+
}
66+
67+
const combinations: {
68+
sats: Satoshis
69+
cents: UsdCents
70+
display: DisplayCurrencyBaseAmount
71+
}[][] = [
72+
[amount, zero],
73+
[negAmount, zero],
74+
[zero, fee],
75+
[zero, negFee],
76+
[amount, fee],
77+
[negAmount, negFee],
78+
[amount, negFee],
79+
[negAmount, fee],
80+
]
81+
82+
// Match settlementDisplayAmount calc to settlementAmount
83+
let matchIndex = -1
84+
for (const [i, [amount, fee]] of combinations.entries()) {
85+
const unit = currency === WalletCurrency.Btc ? "sats" : "cents"
86+
if (amount[unit] + fee[unit] === settlementAmount) {
87+
matchIndex = i
88+
break
89+
}
90+
}
91+
92+
// Calculate settlementDisplayAmount with matched combination
93+
let settlementDisplayAmountAsNumber = 0
94+
if (matchIndex >= 0) {
95+
const [amountToUse, feeToUse] = combinations[matchIndex]
96+
settlementDisplayAmountAsNumber = amountToUse.display + feeToUse.display
97+
}
98+
99+
return {
100+
settlementAmount,
101+
settlementDisplayAmount: minorToMajorUnit({
102+
amount: settlementDisplayAmountAsNumber,
103+
displayMajorExponent: MajorExponent.STANDARD,
104+
}),
105+
}
106+
}
107+
108+
return {
109+
fromTxn,
110+
}
111+
}

src/domain/wallets/tx-history.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { AdminLedgerTransactionType, LedgerTransactionType } from "@domain/ledge
1212
import { TxStatus } from "./tx-status"
1313
import { DepositFeeCalculator } from "./deposit-fee-calculator"
1414
import { PaymentInitiationMethod, SettlementMethod } from "./tx-methods"
15+
import { SettlementAmounts } from "./settlement-amounts"
1516

1617
const filterPendingIncoming = ({
1718
pendingIncoming,
@@ -33,8 +34,11 @@ const filterPendingIncoming = ({
3334

3435
const settlementAmount = toSats(sats - fee)
3536

37+
const priceForMinorUnit =
38+
displayCurrencyPerSat.price * 10 ** Number(MajorExponent.STANDARD)
39+
3640
const settlementDisplayAmount = minorToMajorUnit({
37-
amount: Math.round(displayCurrencyPerSat.price * settlementAmount),
41+
amount: Math.round(priceForMinorUnit * settlementAmount),
3842
displayMajorExponent: MajorExponent.STANDARD,
3943
})
4044

@@ -77,7 +81,6 @@ const translateLedgerTxnToWalletTxn = <S extends WalletCurrency>({
7781
const {
7882
type,
7983
credit,
80-
debit,
8184
currency,
8285
satsAmount: satsAmountRaw,
8386
satsFee: satsFeeRaw,
@@ -96,8 +99,7 @@ const translateLedgerTxnToWalletTxn = <S extends WalletCurrency>({
9699
type as AdminLedgerTransactionType,
97100
)
98101

99-
const settlementAmount =
100-
currency === WalletCurrency.Btc ? toSats(credit - debit) : toCents(credit - debit)
102+
const { settlementAmount, settlementDisplayAmount } = SettlementAmounts().fromTxn(txn)
101103

102104
let satsAmount = satsAmountRaw || 0
103105
let centsAmount = centsAmountRaw || 0
@@ -127,11 +129,6 @@ const translateLedgerTxnToWalletTxn = <S extends WalletCurrency>({
127129

128130
const status = txn.pendingConfirmation ? TxStatus.Pending : TxStatus.Success
129131

130-
const settlementDisplayAmount = minorToMajorUnit({
131-
amount: displayAmount,
132-
displayMajorExponent: MajorExponent.STANDARD,
133-
})
134-
135132
const baseTransaction: BaseWalletTransaction = {
136133
id: txn.id,
137134
walletId: txn.walletId,

test/e2e/servers/graphql-main-server/with-auth-requests.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ describe("graphql", () => {
259259
node: expect.objectContaining({
260260
settlementAmount: 4_000,
261261
settlementCurrency: WalletCurrency.Usd,
262-
settlementDisplayAmount: "40",
262+
settlementDisplayAmount: "40.00",
263263
settlementDisplayCurrency: DisplayCurrency.Usd,
264264
}),
265265
}),

test/integration/02-user-wallet/02-receive-onchain.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ describe("UserWallet - On chain", () => {
409409
expect(pendingTx.initiationVia.address).toBe(address)
410410
expect(pendingTx.createdAt).toBeInstanceOf(Date)
411411

412+
expect(pendingTx.settlementDisplayAmount).toBe(
413+
(
414+
pendingTx.settlementAmount * pendingTx.displayCurrencyPerSettlementCurrencyUnit
415+
).toFixed(2),
416+
)
417+
412418
// Check pendingTx from cache
413419
const { result: txsFromCache, error: errorFromCache } =
414420
await getTransactionsForWalletId(walletIdA)

test/integration/02-user-wallet/02-send-onchain.spec.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import {
4949
UnknownOnChainServiceError,
5050
} from "@domain/bitcoin/onchain/errors"
5151
import { TxDecoder } from "@domain/bitcoin/onchain"
52+
import { SettlementAmounts } from "@domain/wallets/settlement-amounts"
53+
5254
import * as OnChainServiceImpl from "@services/lnd/onchain-service"
5355
import { DealerPriceService } from "@services/dealer-price"
5456

@@ -236,6 +238,13 @@ const testExternalSend = async ({
236238
const pendingTx = pendingTxs[0]
237239
const interimBalance = await getBalanceHelper(senderWalletId)
238240

241+
const pendingLedgerTx = await LedgerService().getTransactionById(
242+
pendingTx.id as LedgerTransactionId,
243+
)
244+
if (pendingLedgerTx instanceof Error) throw pendingLedgerTx
245+
const { settlementDisplayAmount } = SettlementAmounts().fromTxn(pendingLedgerTx)
246+
expect(pendingTx.settlementDisplayAmount).toBe(settlementDisplayAmount)
247+
239248
if (sendAll) {
240249
expect(pendingTx.settlementAmount).toBe(-initialWalletBalance)
241250
expect(interimBalance).toBe(0)
@@ -291,6 +300,13 @@ const testExternalSend = async ({
291300
expect(settledTx.settlementFee).toBe(fee)
292301
expect(settledTx.displayCurrencyPerSettlementCurrencyUnit).toBeGreaterThan(0)
293302

303+
const settledLedgerTx = await LedgerService().getTransactionById(
304+
settledTx.id as LedgerTransactionId,
305+
)
306+
if (settledLedgerTx instanceof Error) throw settledLedgerTx
307+
const { settlementDisplayAmount } = SettlementAmounts().fromTxn(settledLedgerTx)
308+
expect(settledTx.settlementDisplayAmount).toBe(settlementDisplayAmount)
309+
294310
const finalBalance = await getBalanceHelper(senderWalletId)
295311

296312
if (sendAll) {
@@ -540,6 +556,13 @@ const testInternalSend = async ({
540556
expect(senderSettledTx.settlementAmount).toBe(-senderAmount)
541557
expect(senderSettledTx.displayCurrencyPerSettlementCurrencyUnit).toBeGreaterThan(0)
542558

559+
const senderSettledLedgerTx = await LedgerService().getTransactionById(
560+
senderSettledTx.id as LedgerTransactionId,
561+
)
562+
if (senderSettledLedgerTx instanceof Error) throw senderSettledLedgerTx
563+
const { settlementDisplayAmount } = SettlementAmounts().fromTxn(senderSettledLedgerTx)
564+
expect(senderSettledTx.settlementDisplayAmount).toBe(settlementDisplayAmount)
565+
543566
// Check txn details for received wallet
544567
// ===
545568
const { result: txsRecipient, error: errorUserA } = await getTransactionsForWalletId(
@@ -565,6 +588,13 @@ const testInternalSend = async ({
565588
expect(recipientSettledTx.settlementAmount).toBe(recipientAmount)
566589
expect(recipientSettledTx.displayCurrencyPerSettlementCurrencyUnit).toBeGreaterThan(0)
567590

591+
expect(recipientSettledTx.settlementDisplayAmount).toBe(
592+
(
593+
recipientSettledTx.settlementAmount *
594+
recipientSettledTx.displayCurrencyPerSettlementCurrencyUnit
595+
).toFixed(2),
596+
)
597+
568598
// Check memos
569599
// ===
570600
const matchTx = (tx: WalletTransaction) =>

0 commit comments

Comments
 (0)