Welcome

Welcome to our inaugural blog post. In this series, we'll explore a range of topics including our development challenges as well as financial infrastructure with a particular focus on cross-border payments.

I'm Wassily the founder of nxos, and since this is our kickoff, let me start with a quick introduction. Previously, I led the engineering team at a rapidly growing fintech company in Dubai. When I joined, we were a small team of five, but we expanded to nearly 200 in just four years. During that time, we scaled from zero to processing transactions worth well over a billion.

At nxos, we're transforming this experience into as-simple-as-possible, scalable fintech infrastructure. Our focus includes ledgers, reconciliation, card integrations, and, in particular, cross-border payments.

Needless to say, if your company needs fintech solutions in ledgers, reconciliation, card integrations, or cross-border payments, reach out to me at [email protected] to explore how nxos can help.

A simple double-entry ledger with source and destination accounts

Today, we'll build a simple double-entry ledger using PostgreSQL, featuring source and destination accounts. Before we build out the solution, let's clarify the key concepts that underpin this model:

  1. Double-entry accounting ensures that for each transaction, the sum of its postings equals zero. Traditionally, this means each posting has both a debit and credit entry for the same amount.
  2. The source/destination model aligns with people's intuitive understanding of money flow: money has an origin (source) and a destination. For example, when you receive a salary, the source is your employer, and the destination is your bank account.

This model naturally adheres to the double-entry standard: the amount deducted from your employer's account exactly matches the amount deposited into your bank account.

If you'd like to further explore double-entry accounting and general accounting principles, here are two resources I've found particularly helpful:

  1. Beancount's comprehensive guide on the double-entry counting method
  2. Modern Treasury's "Accounting for Developers" series, starting with Part I

For our source-destination model, we'll create a single table named 'move'. We chose this name because it represents the movement of an amount from the source to the destination. The table structure is as follows:

CREATE TABLE move (
    id SERIAL PRIMARY KEY,
    source VARCHAR(255) NOT NULL,
    destination VARCHAR(255) NOT NULL,
    amount BIGINT NOT NULL,
    currency VARCHAR(3) NOT NULL,
    description TEXT NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
​
-- Indexes to list transactions for sender and receiver
CREATE INDEX idx_move_sender ON move(source, created_at);
CREATE INDEX idx_move_receiver ON move(destination, created_at);

For each move, we define a sender and a destination. The sender's balance decreases by the sent amount, while the destination's balance increases by the same amount, making their sum zero.

The source and destination columns reference accounts in the system. An account in this case is simply an entity that can hold a currency and have a balance. There can be a one-to-one mapping between an account and a customer, or a customer can have many accounts. For example, a customer might have both a checking account and a savings account.

Each 'move' includes an amount and a currency field. The amount represents the quantity of currency in subunits to be transferred from the source to the destination account. In our ledger, amounts are denoted in subunits, representing the smallest possible unit of the currency (e.g., cents for USD). This approach avoids floating-point arithmetic issues.