State Management

Lightning Talk: How To Keep Funds In Place - Working With Money In Fintech Apps

Lightning Talk: How To Keep Funds In Place - Working With Money In Fintech Apps

by Bohdan Varshchuk

In this lightning talk presented at Pivorak Conf 2.0, Bohdan Varshchuk delves into the complexities of managing funds within fintech applications, providing insights into best practices and common pitfalls. The main focus is on effectively handling monetary types, ensuring precision in calculations, and maintaining transaction integrity. Varshchuk emphasizes three primary considerations when managing money in applications:

  • Monetary Types: It is crucial to avoid using floats and doubles for money calculations due to potential imprecision from floating-point arithmetic. Instead, using types like BigDecimal is recommended to ensure accurate monetary computations.

  • Precision and Scale: Understanding the concepts of precision (the total number of digits) and scale (the digits after the decimal point) is essential, especially as some currencies can have more than two decimal places. Hard-coding these limits can lead to future difficulties.

  • Custom Monetary Types: Developers should consider creating custom types for handling money, using libraries such as MoneyShem, which facilitates seamless currency conversion and transaction handling by allowing operations on specified amounts and currencies.

Additionally, Varshchuk touches on the importance of accounting for users' balances by considering multiple accounts in different currencies and ensuring that transaction history is maintained to prevent issues like double spending. Key strategies include:

  • Account Versioning: Maintaining a history of transactions allows for better oversight of changes to user balances and helps detect anomalies that may indicate fraud.

  • Understanding Price Fluctuations: Recognizing that prices can fluctuate significantly—especially in the context of cryptocurrencies—is vital for accurate financial modeling. Fees should also be dynamically accounted for based on transaction specifics.

  • Centralized Management of State Changes: To avoid race conditions and ensure data integrity, a single point of management for financial operations should be implemented. This means handling transactions in a controlled manner rather than directly altering balances, which mitigates concurrency issues.

In conclusion, Varshchuk advocates for adopting a culture of best practices in managing money within applications. He advises implementing robust checks to handle unexpected events such as double submissions, thereby safeguarding against large erroneous amounts that could lead to significant losses. He underscores the importance of diligence and precision in all financial transactions to maintain integrity and avoid costly mistakes.

00:00:05.350 Hello everyone! It's nice to see you here at the second Pivorak Conference. It’s great to have new faces joining us in the community. I appreciate you all coming to our meetups, as well as the sponsors who support our conferences and talks, and definitely those who contribute to our summer courses.
00:00:20.590 Today, I will be discussing some really complex topics, which means I only have time to touch on three main points. Each of these points could warrant a talk of its own, but I will try to briefly go over the key mistakes and best practices regarding funds management and dealing with money in your fintech applications.
00:00:49.809 The first topic I want to cover is monetary types. There are three main things you need to know when handling money in your applications. The first and most common mistake is avoiding the use of floats and doubles for money calculations. Can anyone tell me why that’s an issue? Raise your hands, please.
00:01:57.700 For those who didn’t raise their hands, the answer is that doing so can lead to imprecise results due to how floating-point arithmetic works. When using regular floats in Ruby, non-integer values can be imprecise, and this becomes even more apparent when you deal with monetary types that involve more than two decimal places. To avoid this pitfall, you should use the appropriate type, such as BigDecimal. I’ve heard from some of my peers who faced problems related to this issue in production; I've encountered similar issues early in my career.
00:02:22.509 The second consideration relates to precision and scale. Precision refers to the total number of digits you store in your database, while scale indicates how many of these digits are after the decimal point. The common issue is that we often think in terms of fiat currencies like dollars and euros, which typically have only two decimal places. However, some currencies, especially cryptocurrencies and certain asset classes, can have more than two decimal places. If your database is hard-coded for just two decimal places, changing this later can be quite challenging when working with applications that involve various currency types.
00:02:33.129 The third obvious recommendation is to use appropriate types for money, or better yet, create your own types instead of relying on default ones. I would also like to mention a library called MoneyShem, which provides a beautiful abstraction for handling currencies and monetary values. It works out of the box with most self-specified currencies and makes application integration quite straightforward.
00:02:43.420 You simply create your money instances by specifying the amount and the currency, and it can convert between different currencies automatically, as long as you connect the appropriate driver for currency conversion. In most applications, this is usually everything you need. If you require custom currency handling, it's relatively simple to create your own monetary type from scratch, but doing so gives you complete control over the implementation.
00:03:05.500 Now, let’s discuss the accounting of our income. In any application, regardless of the domain, we don’t have money appearing from nowhere; we cannot just shake trees and expect dollar bills to fall. Every income should be calculated in some fashion. So where does the money come from, and how do we model that? Firstly, we need to think about accounting. When we talk about a user's balance, it’s important to remember that they might have multiple accounts in different currencies. A user’s balance is not simply about the total amount they have; it varies by the currency.
00:04:06.760 Additionally, when we say that a user has a balance of one thousand dollars in their USD account, the problem arises when we perform transactions. We are not executing atomic operations; we need to ensure that we have collected balances appropriately while logging the user’s balance. We can’t allow users to spend money multiple times without proper confirmations of successful withdrawals, whether those are through blockchain transactions or traditional systems.
00:04:47.620 Another important concept is account versioning. We need to maintain a clear history of funds to understand where the money goes and how it changes over time. The most common approach to manage this is to keep a transaction history using existing libraries or by implementing one from scratch. The key idea here is to track changes in balance consistently, from zero to ten thousand, for example. Some simpler applications only store the current balance and version numbers, changing the version each time funds are added or deducted. This can help prevent double spending and raise an error if there's an attempt to spend already spent funds.
00:05:34.660 If operations affect the account’s history, for instance during suspicious transactions, checks must be put in place to ensure integrity. If a user attempts a withdrawal and their actual balance in the database doesn’t match the expected history, it might indicate fraud or an error, especially if adjustments were made directly by someone in the development team.
00:06:19.150 Moving on, we should note that account balances in different currencies are not the same as the price, fees, and rates. Prices fluctuate based on conditions like regional mandates or promotions. They can often have more decimal points compared to standard currencies.
00:06:32.560 Similarly, when dealing with exchange rates, it’s uncommon to expect static values for extended periods, especially with cryptocurrencies. When performing transactions, it’s prudent to use a buffer to account for potential price changes within a short timeframe.
00:07:06.210 Fees are usually more variable than we anticipate—they can incorporate static amounts, a percentage of the transaction, or differ based on the third parties involved. If we fail to factor this into our applications from the start, we may find ourselves writing complex logic later.
00:07:50.800 Lastly, I want to talk about the principle of having a single point of management for state changes in financial transactions, often referred to as 'one ring to rule them all.' In an ideal scenario, we want to avoid situations where multiple threads or workers modify the same balance simultaneously. By implementing a centralized service to handle balance updates, we can ensure more explicit control and prevent inconsistent data states.
00:08:29.910 The idea is to never directly manipulate the balance itself but to use operations that adjust the balance in a controlled manner. For example, a system might manage balance changes through operations like 'add funds' or 'deduct funds.' Each time an operation is executed, the balance is updated based on the difference rather than directly setting a new value. This helps mitigate issues related to race conditions where multiple actors might try to access or alter the same balance simultaneously.
00:09:27.820 We should also incorporate fallback strategies in cases where transactions are attempted concurrently. This is particularly important to avoid scenarios where a user might receive funds while another transaction shows insufficient funds.
00:10:00.000 In summary, when dealing with money management in applications, we must adopt a culture of best practices. This includes keeping immutability in our state changes, never directly decreasing balances without first locking them in a controlled manner, and diligently managing our money flow cycles.
00:10:22.830 In conclusion, always be prepared for unexpected issues like double submissions and ensure proper checks are in place to prevent erroneous transactions—it's better to catch an issue early in the transaction process than to process a large erroneous amount that can lead to significant losses.
00:10:50.450 Thank you so much for your attention! I appreciate your time. To express my gratitude, I have a small gift for you. You all deserve it!