Pivorak Conf 2.0

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

Pivorak Conf 2.0

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!