blink
blink copied to clipboard
Amount-related properties for transactions in our database
Description
With the new hedged-USD & display-currency work we're starting to get things confused with the different data fields we store in our database for a transaction.
Key issues (described below):
- storing ledger-related values (debit/credit and fee)
- storing the transaction currency vs. the conversion currency (for hedging/limits) vs. the display currency
- unit for display currency
- separating amounts and fees
Sample Transactions
BTC payment to external invoice (freecorn-left | local-dev-right)

USD payment to external invoice (freecorn-left | local-dev-right)

Issues
1. Value stored in debit/credit & fee props
The values currently stored in debit/credit should be integer values that represent units of that particular wallet currency e.g. sats for BTC wallets and cents for USD-hedged wallets.
The fee property is usually relevant to the debit/credit values of a transaction as it is sometimes used as the main amount for related transactions (bank fees, reimbursements etc.).
Current state:
feeis always denominated insatsfeeUsdis always demonimated incents(new code) orusd(prod code)
Question: should fee always be sats, or should it be denominated in the same currency that debit/credit are denominated in?
2. Transaction currency vs. BTC equivalent vs. display currency
With our new model we now have 3 different types of currencies that will be present for all transactions:
- BTC: relevant in all except intraledger USD-to-USD txns
- USD: hedged value relevant for USD wallet, un-hedged equivalent relevant for limits & display conversions
- Display: relevant for displaying in UIs, is currently USD but can be derived from USD as well when not USD e.g. CRC
Questions:
- should we always store amounts that map to all three types separately in each transaction?
- should we overload certain properties (
debit/credit,usd), or always distinctly store all 3?
3. Unit for display currencies
The values for the usd property are currently denominated in dollars and used by interfaces like the mobile app to display fiat equivalents. These values can be floats currently.
In our new code, we've been adding the cents-equivalent usd value to this field instead which is correct for the converted fiat amount but wrong for what our UIs expect for the display currency amount. These values are also now stored only as integers from our domain.
Question: how should we handle this separation?
4. Recording amounts vs. amounts + fees
In some places we store the pre-fee amount (e.g. feeUsd in new code) and in other places we store amount + fees (e.g. in feeUsd older code, sats in very old transactions).
Also separately:
debit/credit = amount + fee (for payments)
debit/credit = amount - fee (for receipts)
Question: what should we standarize on for this?
Some notes @dolcalmi and I took from a call today thinking through some of this.
For payment & fee_reimbursement transaction types:
{
feeUsd (prod)
displayCurrencyFee (in dollars)
feeUsd (dev)
usdFee (in cents)
usd (prod)
displayCurrencyAmount (in dollars)
usd (dev)
usdAmount (in cents)
fee (prod + dev)
satsFee
sats (prod-old)
satsAmount + satsFee
satsAmount (prod-new: does not exist)
satsAmount
debit (prod + dev)
satsFee + satsAmount
credit (prod + dev)
satsFee + satsAmount
}
{
walletCurrency: {
debit
credit
fee
currency
}
btcCurrency: {
amount
fee
}
displayCurrency (CRC) {
name
amount
fee
}
}
[CRC]
paymentFlow = {
btcAmount
usdAmount
displayCurrency
}
debit = amount + fee
credit = amount - fee
Limits???
I think it makes sense to be redundant in our data and be as clear as possible. This would allow for flexibility in how we display the tx to the end user as well as greatly reduce the cognitive load of overloading the terminology. The downside would be storage space, but I dont think this will be a problem with mongodb. We could have the below tx schema:
- debit | credit: BigInt
- accountingCurrency: "USD" | "BTC"
- satsAmount: BigInt
- centsAmount: BigInt
- satsFee: BigInt
- centsFee: BigInt
- displayAmount: BigInt
- displayFee: BigInt
- displayCurrency: any
I think it makes sense to be redundant in our data and be as clear as possible.
Agree, proposing an alternative structure just to have multiple options
{
amounts: {
btc: {
amountInBase: BigInt
baseUnit: String
currencyCode: String
}
fiat: {
amountInBase: BigInt
baseUnit: String
currencyCode: String
}
display: {
amountInBase: BigInt
baseUnit: String
currencyCode: String
}
}
}
- debit | credit: number # forced by medici
- accountingCurrency: "USD" | "BTC"
- satsAmount: BigInt
- centsAmount: BigInt
- satsFee: BigInt
- centsFee: BigInt
- displayAmount: BigInt
- displayFee: BigInt
- displayCurrency: any