E.g. If I ever see a monetary value stored in something else than integers I'm going to run away screaming (thank you Rust decimals represented as JSON floats). It's always integers unless you have a VERY good reason to do otherwise (though exported view can be in anything, even in weird bitcoded formats).
FX exchange. Resolution of FX isn't a point-in-time thing, things like buyer rate-in-time, seller rate-in-time, agreement, agreement tolerance, agreed upon resolution timestamp come in the effect.
Immutability - that's why you want to have event sourcing everywhere that touches money:
# Resolved stream
A -> B -> E
# Actual stream
A0 -> Edit(A0, A) -> B -> C -> D -> Rollback(B) -> E
Though in the end Fintech != Fintech. I worked at Fintech where money was treated like a baggage, and in other where money was a central point of everything.It seems like a clever idea (fast integer math, no rounding problems for addition and subtraction), but it'll bite you incredibly hard if you ever stumble upon an edge case such as working with a partner that has a different implied number of digits for a given currency. This is especially relevant for stablecoins, which often have a different number of implied decimal digits than the "fiat" currency they represent.
Also, consider representing amounts as a string type in JSON-based APIs. JSON does not specify decimal precision, so you (and all your users/vendors) will always have to make sure your parser/serializer doesn't internally lose precision by going via floating point. This can get ugly fast, and while a string seems conceptually less neat, it completely bypasses that problem. (Some will call this an anti-pattern [1], but I'd rather not fight this particular battle for ideological purity on the shoulders of my users or shareholders.)
[1] https://blog.json-everything.net/posts/numbers-are-numbers-n...
For example the parts talking of retries, idempotency, event ordering, etc. This applies to all systems that require any degree of accuracy, even if no money is directly involved. I've seen so many systems built on the assumption that "we can always retry", but you can only retry if you fail cleanly in the first place, and if the downstream system offers the same level of idempotency that you think it does. Quite often these are not put to the test.
I would recommend anyone starting in fintech to take some time to understand accounting principles and the ledger in a bit more depth than just debits vs credits - this is likely what is most unfamiliar to programmers.
Also financial software is very data-heavy and I learned more about databases in my time working in fintech than the 15 years before that. I think going into a bit more detail about even the basics (indexes) will save a lot of headaches.
I see webhooks documented all the time, but I have yet to use them in practice, nor have my customers requested them. Is the above not true, or are they widely used in some sectors and not others?
In the HFT space you save some wire space if you can commit to a consistent exponent for some {slice} up front (think instrument/tick-size/asset-class/exchange/feed/server/whatever/...) such that you only need to send the mantissa and your clients can have a hard coded exponent. However, in similar spaces it's often worth the extra uint32 to send a on-the-wire exponent such that things _can_ change and you aren't hamstrung later by earlier "we only need cents now!" design choices when, e.g., you suddenly need to support bitcoin/... prices to full precision. (your users will thank you when they don't have to coordinate a breaking change when you want to adjust your fixed exponent)
Many of these predate the widespread knowledge of idempotency, so often idempotency keys are hacked together by joining various, hopefully globally unique fields, except that they never quite are. (You can look behind the curtain sometimes, e.g. when your bank does not let you transfer the same amount to the same recipient account on the same calendar day.)
I've spent many hours explaining how idempotency is supposed to work, and why it's important. Most teams understand the need for it, but very few thought about it up front.
Why would that be a problem? You just transform the values when interacting with their API.
Any good resources you would recommend to learn more about this?
I just published Fintech Engineering Handbook distilled from 6 years of tears, sweat and swears.
It’s a free ~25-page resource with various hints and patterns around handling money.
Tell me what you think!
other than that, peruse the commits on the source [1], or wait for the author to respond.[0]: https://mas.to/@krever/116814803588993437
[1]: https://github.com/Krever/fintech-engineering-handbook/commi...
I largely agree with TFA: Round explicitly and consistently whenever you cross a boundary, i.e. database persistence and internal API calls.
Use whatever works for your required business case internally (i.e. inside of procedures calculating some function of one or more input amounts). This can be regular old floats/doubles if you absolutely know what you're doing, or BigDecimal if you aren't and would rather suffer slightly slower performance than having to talk to an auditor about IEEE 754 rounding modes, or even minor-amount integers (yes, even though I just said to not use them – but you'll want to ABSOLUTELY NEVER leak them outside of your system, including your data/analytics pipeline, which might have different ideas about financial amounts than your business logic implementing a nice custom monetary type).
Its at least 80% organic artisanal writing and maybe 20% AI when I needed help with grammar, completeness, broader perspective and everything around.
In C# e.g., there is type decimal for those computations.
I don't care if the balance is one million, before that ACH can process, every single dollar can be (a) wired out, (b) cleared out by yesterday's ACHs (bills, autopay, whatever) and checks, or (c) spent at debit/ATM.
I probably shouldn't tell you why I know that some fintechs don't address this.
I was CTO of a FinTech where I built the whole software stack from scratch: the lessons in the book are mostly correct. I say mostly, because as always, there is a lot of "it depends" to take into consideration for your particular project. For example, I chose to not use event-sourcing to avoid the whole state computation issue. A standard append-only audit trail can do the job.
You can't guarantee exactly-once delivery but you can construct effectively-once processing, and that is what you really want.
Store every request and response : absolutely, and not only when consuming APIs, but when collecting any information from the outside world (and, if you can, also log every intermediate transformation step within your perimeter). Content-adressed buckets + a relational table are great for this.
The text also does not mention anything about data lineage. What happens if a vendor updates some data mid-day that you absolutely need to be aware of? You need to be able to account for that, while also re-playing computations that used the old values and get the same result. It's not a particularly hard problem to solve, but it takes some thought.
What user xlii said about not storing monetary amounts as floats is a common IEEE 754 issue. And while it's true that financial tracking should be done through immutable logs or event-based records, I don't think every surrounding service needs to be built with event sourcing. I think it's enough to apply it only to core logic like ledgers, settlements, orders, and executions. Looking at xlii's comment, it seems like a technique that only becomes viable when the modeling is successful.
User lxgr's comment points out that it's a minor-unit issue. If JSON numbers are parsed as floats by the language or parser, precision can be lost. Usually people send values with a separate decimal places field. However, I've heard that in HFT, they don't do that because the overhead itself is too costly.
And antonymoose's comment aligns with what many books say. That's why designs like this are common in FX or API contexts. It feels like protocol design, doesn't it?
Putting it all together, everyone's right within their own domain. While I think it'd be great to have someone like xlii as my senior programmer, I also feel like I wouldn't be able to design such a complex system myself. In that sense, everyone's statements are valid, and it's interesting to see how opinions diverge depending on the domain. Is this what expertise looks like
Looking at all this, it seems like you can roughly infer where a programmer is coming from based on their experience. Sometimes programming doesn't feel like finding the right answer, but more like choosing a worldview
Watching how programmers model their domains on HN is always fascinating. Sometimes I click on their profiles and add their domain knowledge to my own personal wiki, thinking I might use it someday
Let's say I operate with a 4 decimal expectation and your API expects 6, is there any way to reconcile that outside of documentation and or metadata ? (which would be the same issue I guess whatever representation is used ?)
The art is in making those points well-defined and rare enough to not cause large discrepancies, but frequent enough to avoid ballooning arbitrary-precision numbers across databases and services that might not be able to handle them.
That's putting it politely. Honestly, I think this "handbook" was mostly written by an LLM.
For example, in the immutability section we have this:
"Separating PII from financial data lets you honor erasure without losing the financial history you’re obliged to keep."
In a financial organisation the two go hand-in-hand for obvious KYC/AML reasons.Keeping the financial data whilst trashing the customer names, addresses etc. instantly on-demand before the expiry of the relevant time periods is going to leave your entire organisation with a very bad day in the office if a $lawful_body comes knocking for the data to trace a crime.
People going to work in a Fintech should not be relying on a random "Handbook" written by an unknown person in an unknown jurisdiction.
People going to work in a Fintech should only ever work in accordance with their employer's internal handbooks/guidelines/etc which will have been written in conjunction with their firm's lawyers and compliance people to ensure it complies with the laws and reporting requirements in the jurisdiction(s) in which their employer operates.
Still, even if you do: Chances that your users are just going to assume you're conforming to ISO 4217, some national standard, or your competitor that they're already integrated with are pretty high, so I wouldn't take the chance. Pick something that doesn't have to be documented instead.
Floating-point precision has too many gotchas for being suitable to store Decimal types, especially for the Currency use case.
Where does TFA recommend that?
As I see it, it recommends separating PII data you'll eventually have to delete from that you'd probably want to keep forever (including data factoring into your accounting equations/invariants), so that you can delete the former after the relevant recordkeeping periods have elapsed.
> People going to work in a Fintech should not be relying on a "Handbook" written by an unknown person in an unknown jurisdiction.
Sure, but they should also not blindly ignore any ideas and practices presented, or avoid looking beyond their own organization. Ideally, they'll then try to reconcile what they saw with their own knowledge and local regulations etc.
> People going to work in a Fintech should only ever work in accordance with their employer's internal handbooks/guidelines/etc which will have been written in conjunction with their firm's lawyers and compliance people to ensure it complies with the laws and reporting requirements in the jurisdiction(s) in which their employer operates.
Sure, in a world in with only perfect and error-free organizations, that seems like a reasonable approach. But how does one get there without having a conversation such as this one?
Unless its your job to architect stuff, in a financial firm you don't go looking around for ideas and practices.
You comply with your employer's practices end of story.
If you like looking up ideas and other people's practices then a heavily regulated environment is probably not the place for you.
> how does one get there without having a conversation
"having a conversation" about new ideas/practices in a regulated firm will involve lawyers and the compliance department.
More than likely that "conversation" will be above most people's pay grade. So you're better off just not wasting your time and adhering to your employer's existing practices.
And for everyone else, its an expensive and high-friction conversation to have if you want to change existing practices.
> You comply with your employer's practices end of story.
What if you're the employer ("first engineer" etc.), and there are no practices yet? Fintech almost by definition sometimes includes doing things from scratch because some existing solution or incumbent organization isn't working that well anymore.
> Unless its your job to architect stuff
Which seems to be the target audience/scenario for TFA.
In that scenario the practices will still come first. You're not going to be doing any coding or systems engineering until you've got compliance signed off. You're going to be spending lots of time with lawyers and compliance people.
> Fintech almost by definition sometimes includes doing things from scratch
Yes, but cut through the noise of the typical Fintech fancy website and app and you're still staring straight down the barrel of spending 80% of your time on regulatory compliance.
Try as you might there are only so many ways you can re-invent the wheel for dealing with hard-facts legislation.
And if your lawyers and compliance people are actually telling you that you can absolutely not do any financial processing yourself, that the only possible way to be compliant is to license <incumbent product xyz> (unfortunately only available in COBOL) etc., you might not actually be working in a fintech, or at least not in the kind this guide seems to be targeted to.
Frankly, this kind of attitude is exactly why banking and payments is as fossilized as it is in some countries, and why fintech is eating their lunch in many cases. There has to be a balance between trying new things and doing what everybody else is already doing.
Welcome to the Fintech Engineering Handbook. This resource aims to describe the most important patterns used in software engineering, where money is the primary focus of the system. It can be read in full to get a comprehensive understanding or in parts when dealing with a particular problem.
It’s meant as a living document and contributions are welcomed.
Everything you will read below is a way to adhere to the three principles:
Before you can move or record money, you have to represent it. These are the decisions about how a monetary value is modeled, stored, computed and converted. Getting them wrong means every layer above inherits the error.
Money representation is one of the most fundamental decisions in financial systems. There are four primary ways to do it:
BigDecimal let you control the precision of a computation precisely. The code is predictable and we get to decide where and how rounding happens. It fits intermediate work like FX or pricing math, where many operations chain together.1234. Crypto uses the same integer-smallest-unit idea (satoshis for BTC, wei for ETH), but with two twists: the precision is per-asset and defined by the token itself (e.g. an ERC-20’s decimals), often 18 digits, and the resulting magnitudes overflow 64-bit integers, so you need arbitrary-width integers to hold them.Selecting one or the other depends on the class of the system and its responsibilities. There is no rule of thumb here, other than not using floating points. These representations are not mutually exclusive either - how you store an amount and how you compute with it are separate decisions, and a system often combines them, e.g. integer storage with BigDecimal for intermediate computation.
The same care applies when an amount is being serialized. A bare JSON number is an IEEE-754 double in most parsers, so serializing money as a number reintroduces the floating-point problem at the edge, no matter how carefully you represent it internally. Send money either as a string ("12.34") or as an integer in its smallest unit.
Principles touched:
Principles touched:
Money can’t be represented as a number alone - it comes paired with a currency. There are a few nuances when it comes to handling currencies.
Money newtype (struct, class, record etc.) minimizes the chance of errors.(network, contract address) or similar.Principles touched:
FX (Forex, foreign exchange currency market) rates allow us to convert money between currencies.
Principles touched:
Once represented, money movements have to be recorded in a way that balances, survives audit and can be reconstructed years later. This is where the books, their timestamps and their history live.
Double-entry bookkeeping is a widely used way to store financial transactions as a list of entries in the form of (credit account, debit account, amount) (this is a compact form; the classic representation uses a separate debit and credit row per movement). Because every entry moves the same amount out of one account and into another, the books always balance - money is only moved, never created or destroyed.
assets = liabilities + equity) holds and each account has a defined side on which it increases. In practice you also need income (revenue) and expense accounts - e.g. to book a fee as revenue or a write-off as a loss (assets = liabilities + equity + revenue - expenses).Principles touched:
Transactions will usually have at least two, sometimes three timestamps associated:
The first two will almost always diverge:
Example: a card payment happened at T1 (value time), you recorded it at T2 (booking time), but the payment provider transferred money to your account at T3 (settlement time).
Business and business-consumed reports usually care about value or settlement time, while booking time is useful for traceability.
Principles touched:
created_at loses information you can’t reconstruct later.Financial systems are subject to regulatory scrutiny in the form of various audits. Some of the things that might be verified during an audit:
To answer those and many other questions, financial systems have to keep track of not only the current state but the full history of how that state came to be. This history is the audit trail: a record of everything that happened, detailed enough that any balance, report or decision can be explained and reproduced from it.
A useful audit trail captures, for every change:
Money movements are the obvious subject, but manual interventions, configuration changes (fee schedules, rate sources, limits) and permission changes need trails too.
The why is often itself the output of a decision (e.g. a compliance check or risk score). Recording just the outcome (“blocked”) rarely satisfies an audit because you’ll be asked how that outcome was reached. If that logic lives in a decision table or a rules engine (DMN, Drools, Decisions4s) instead of being buried in imperative code, the decision becomes a structured, replayable artifact that says which rules fired, on which inputs, with what result.
Principles touched:
Event sourcing is probably the most principled and systemic approach to building an audit trail. In ES, instead of storing current state with a log next to it, you store only the events and derive state from them. The double-entry ledger is an example of this pattern applied to money - balance is never stored, it is calculated from the stored entries. With this approach the trail is a primary artifact and cannot drift away from reality.
A few practical notes:
In other words: event sourcing is a very good solution when an audit trail is required, but it comes with a very high price in terms of system complexity.
Principles touched:
An audit trail that can be edited proves nothing, hence records can never be updated or deleted. Our log must be append-only, and every correction should be a new record (see below).
Immutability is an invariant, and the usual toolbox applies:
UPDATE/DELETE at the database-permission level.When building a real system bugs are unavoidable and might require you to fix the event log/audit trail. In those cases it’s sometimes easier to update the trail in place instead of keeping it strictly immutable. To balance those two worlds it’s important to understand your reporting schedule and obligations - usually data has to be kept in stone only once it has been reported, e.g. when the financial statement has been shared at the end of the month. Until then you might still be able to modify your data in place, if you detect the problem and fix it before it leaves your system.
Principles touched:
Mistakes still happen, for example a wrong amount gets posted or a transaction lands on the wrong account. Immutability means fixing forward - post a new compensating entry and link it to the record it corrects, in both directions.
The last point is particularly important - when posting corrections/reversals you will need to decide whether to backdate the event (specify a value time in the past) or not. Here a lot depends on the reporting schedule again - usually you won’t be allowed to backdate anything to an already closed period, because it was already reported to the external world.
Principles touched:
A money operation is rarely a single write. It spans steps, concurrency and failure, and has to stay correct - never inventing or losing money - through all of it. These are the patterns that keep a single flow correct, from the invariants it must preserve to surviving a crash mid-way.
In any system there exist special properties that must always hold - we call them invariants. One such invariant is the accounting equation mentioned above. Your business stakeholders might define many such conditions that then have to be enforced.
There are 3 primary ways to enforce invariants:
What’s important: those methods are complementary and you will usually use all of them side by side to achieve the desired level of trust. By construction is the strongest but cannot express everything (especially cross-aggregate or cross-system invariants), runtime checks catch violations at the point of occurrence, and post-factum is the only one that catches bugs that already shipped - but catches them late.
Principles touched:
In most cases your transactions will require interaction with the external world. For example, you might need to run compliance checks before allowing a user to withdraw funds, or you need to register the withdrawal in an external system.
In such cases you also have to avoid race conditions - spending the same money twice, or discovering “insufficient balance” only after the external world interaction already happened.
To address this, systems implement funds reservation (also known as hold-and-release), where funds are first reserved for a particular transaction before the external interaction starts. Once it completes, the reservation is settled and the transaction proceeds; if anything goes wrong, the reservation is released and the funds return to the available balance.
This pattern introduces a distinction between two balances: the total balance (everything the user owns, including reserved funds) and the available balance (available = total - reserved). Balance checks and new reservations are made against the available balance, which is what prevents the same funds from backing two transactions.
A few practical notes:
Principles touched:
An overdraft happens when an account balance goes negative. Overdrafts come in two kinds:
Unintentional overdrafts happen even in correct systems, because the external world doesn’t ask for permission: a settlement comes in higher than the reserved estimate or a reversal lands after the funds already left. Funds reservation reduces the window for overdrafts but cannot eliminate it.
Forbidden is not the same as unrepresentable. It’s tempting to encode “balance is never negative” at the type or storage level as an unsigned integer or a CHECK (balance >= 0) constraint. But when we are forced to accept a negative balance, a system that cannot represent it will either crash mid-flow, silently clamp the balance to zero (inventing money), or do something similarly wrong.
Put differently, “balance >= 0” is just an invariant and the usual toolbox applies: enforce it at runtime when authorizing transactions, detect violations post-factum with monitoring and reconciliation - but don’t force it by construction. When an overdraft is detected, it’s a signal to investigate but not necessarily a bug.
When an overdraft does happen, we have to book it and recover explicitly, e.g. by netting it against future deposits, requesting repayment, or writing it off - as an explicit compensating entry to an expense/loss account.
Principles touched:
In a distributed system it’s impossible to guarantee exactly-once delivery - any call can be interrupted and we won’t know whether it reached the other side or not. To make sure a message is delivered, we have to retry every such call. But in doing so we risk delivering it more than once, hence its processing needs to be idempotent - the same message delivered twice must trigger the processing only once.
Idempotency matters on both sides - when you make calls and when you receive them. Keep it in mind every time you consume or expose an operation.
Principles touched:
A money flow rarely happens in a single step. A withdrawal might reserve funds, run compliance checks, register the operation in an external system and finally settle. Such a sequence is stretched across time and can die between any two of those steps, so the safe assumption is that it will: assume failure every two steps. A flow can therefore never assume it runs to completion in one go, and a half-finished one must always land in a recoverable state, never an inconsistent one.
You can use a durable-execution engine (e.g. Temporal, Camunda, Workflows4s, AWS Step Functions) or hand-roll your own persistent state machine.
Principles touched:
Interacting with the external world - whether in the form of 3rd party providers (payments, KYC, AML, banks, custodians, etc.) or internal services - is unavoidable. Our job is to build a system that stays correct regardless of how unreliable those dependencies become.
Sooner or later you will have to call someone else’s API, e.g. a payment provider, a custodian, a blockchain node or a KYC vendor. You don’t control its code, its quality or its uptime, so the safe default is to assume it will misbehave and to build defensively around it.
200 carrying an error body), inconsistent pagination, custom date formats. Don’t get frustrated by it; treat it as the job rather than the exception.Principles touched:
Webhooks are the most common way to receive signals from external systems, but processing them safely is not trivial. While we focus here on webhooks (HTTP endpoints you expose, called by an external system with a payload defined by that system) many of the points apply to other transport methods as well.
There is a recurring theme here: don’t trust the webhook. Treat it as a hint that something happened, not a trustworthy account of what happened.
Principles touched:
It’s quite often a requirement to let the external world know about changes in our system in a reliable way - by publishing a Kafka event, dispatching a webhook call or through a plethora of other means. The problematic part is reliably: we have to ensure at-least-once delivery, and those channels don’t fit the usual transactionality model we tend to rely on. Without transactionality we risk either:
The textbook answer is a 2-phase commit/distributed transaction, but it’s rarely used due to its complexity and the lack of a good way to standardize and reuse the approach. The practical options:
Whichever mechanism you pick, delivery is at-least-once - the relay or connector can publish and then crash before recording that it did, re-sending on restart. Consumers must therefore be idempotent and deduplicate on a stable event id (see idempotency).
Principles touched:
Any system that relies on external data is prone to data drift - a situation where one system doesn’t match the other. For example, you might miss a webhook, or a transaction might be posted to the ledger but not reflected in the external provider’s system. In all such cases we need reconciliation: a process that aligns the two systems. While we say “two”, in practice it can be more than that, e.g. ledger, payment processor and the bank, but this doesn’t change anything in how to approach the problem.
Principles touched:
The patterns so far keep the data correct. But a money system also has to constrain who is allowed to act on it, and prove after the fact that the process was followed. This is where the No trust principle turns inward and your own operators and engineers are a trust boundary too, just like external providers and internal components. An auditor examines these controls alongside the books themselves.
Some actions are too sensitive to leave to a single person, regardless of how trusted they are. Splitting them is the oldest control in finance, and it takes two related forms: segregation of duties (no one person owns a whole process) and four-eyes / maker-checker** (a specific action needs a second person to approve it before it takes effect, also called dual control).
Principles touched:
Who can do what is itself part of the system’s state, and it changes over time as people join, move teams and leave. It’s not enough to know who can touch funds today; Auditors will also ask how they came to have that access.
Principles touched:
In a regulated environment we usually have to audit how code reaches production and so know who reviewed a change, who approved it, when it shipped, and so on. Your version control and CI/CD systems are a great help here if done right.
Principles touched:
Tests matter everywhere, but in a money system they matter more. The difficulty is that you usually can’t enumerate the expected outputs - the space of operation sequences is too large and the interesting failures live in the combinations. The approaches below are ways to gain confidence in the correctness of our system. Treat it as a restaurant menu from which you can choose the techniques with the most impact on your system.
Principles touched:
The hardest part of joining fintech is often not the code but the vocabulary and concepts behind it. The field is full of words that sound ordinary but mean something precise, and acronyms that everyone around you uses without ever expanding.
Caveats: terms a layperson already knows (deposit, withdrawal, transfer, currency) are skipped, and so are the exotic corners you can learn when you get there. We try to focus on the most important terms. Where the handbook already covers a concept properly, the entry links to that section instead of repeating it.
1234) (see Precision handling).No single book covers money systems end to end, so the list below is grouped by layer. Each entry notes what it covers and who it’s pitched at, so you can pick by what you’re missing.
Accounting & ledgers
Payments & cards
Markets & trading
Crypto
The engineering half
KYC & AML
These are written for compliance professionals rather than engineers, so reach for them only when you need the domain itself, not the integration.
The body of this handbook takes each pattern on its own, which makes it hard to build holistic understanding and intuition. This appendix walks through common flows that act as simplified but representative examples of what you will see in a real system. A production implementation would have more steps, more failure branches and more bookkeeping, but the simplified version should be enough to get the general idea. They cover the three directions money moves: out of the system (a withdrawal), into it (a card deposit) and within it (a conversion).
A user asks to withdraw 0.5 ETH to an external address. This is the richest of the three because money is leaving the system through an irreversible external effect - once the chain confirms the send, there is no taking it back.
Where it gets interesting: suppose the actual network fee comes in higher than the estimate you reserved. The settlement can push the account negative. You book the overdraft and recover it explicitly (see Handling overdrafts). And if the process crashes between broadcast (step 4) and confirmation (step 5), resumability plus idempotency are what let it pick up by querying the chain rather than sending the funds twice.
A user tops up their account with a card payment through a payment service provider (PSP). Money is coming in, which shifts the hard part from “don’t send twice” to “don’t trust what the outside world tells you, and don’t credit money that hasn’t really arrived.”
Where it gets interesting: the whole flow is built on not believing the happy-path signal. The webhook is a trigger, not a fact; the clearing account refuses to recognize money until it has actually moved; reconciliation verifies the PSP against your own books rather than the other way around. Every step is an application of no trust.
A user converts 1,000 EUR into USDC and earns a small promotional cashback on the trade. Money moves entirely within the system, so there is no unreliable external rail - instead this flow stresses the representation layer (precision, rounding, currencies, rates) and the sharpest form of the no invented data principle.
(network, contract address), not a bare code, and is not interchangeable with the fiat it is pegged to (see Currency handling). The system forbids cross-currency arithmetic; the only bridge between the two amounts is an explicit conversion at the controlled rate.Where it gets interesting: the cashback and the spread pull in opposite directions on the same posting. The spread is money the user loses to you (revenue); the cashback is money you give the user (an expense). Both are real, both go through the books, and both round - so the one transaction has to satisfy no invented data (the books still balance, nothing minted) and no lost data (every residual tracked) at the same time.