Integrity
An append-only ledger is the source of truth. Cash, bonus, and locked buckets are accounted for separately, and atomic operations prevent half-completed transfers or double spending.
C++ collaboration ยท Transaction platform
A multi-tenant iGaming wallet designed around atomic money movement, provable history, replayable events, and resilient operation.
Overview
MonteCarlo Wallet is a multi-tenant, game-agnostic transaction service designed to support casino, betting, and poker products across multiple operators. Games remain stateless and never own a player's balance. Bets, wins, bonuses, deposits, withdrawals, and transfers pass through one authoritative ledger, with atomic and idempotent operations preventing partial money movement or duplicate processing.
Operators can connect their own payment, KYC, AML, and responsible-gaming providers through defined service boundaries. The platform concentrates on the guarantees a game supplier must provide: correct balances, traceable transaction history, recoverable events, and evidence that can be checked after a failure or audit.
Role
I developed the wallet as part of a two-person collaboration. My work included the game-facing transaction flow, reserve, settle, and cancel behaviour, wallet integration, testing, and debugging. I also worked with the wider architecture and learned how individual operations fit into a reliable service rather than treating the wallet as a simple balance counter.
The repository is shared work, so this page describes the system and my contribution without claiming sole authorship of every subsystem. Building and testing it gave me practical experience with idempotency, persistence, event delivery, reconciliation, recovery, and the stricter engineering expectations that apply when software moves money.
Design focus
An append-only ledger is the source of truth. Cash, bonus, and locked buckets are accounted for separately, and atomic operations prevent half-completed transfers or double spending.
A SHA-256 audit chain makes historical changes detectable. Stored events can be replayed by identifier range or player, while reconciliation checks ledger, transaction, outbox, and liability consistency.
Pre-commit policy checks support self-exclusion, player limits, AML holds, deposit limits, maker-checker withdrawal approval, and segregated balance reporting.
A transactional outbox protects event delivery to Kafka. The platform also includes failover, point-in-time recovery, replay tools, health checks, metrics, and distributed tracing.
Implementation
Worked with the path from a game request to wager reservation, result settlement or cancellation, and the player's refreshed balance.
Helped connect the roulette and slot clients to consistent wallet operations and error states, keeping game logic separate from balance ownership.
Tested accepted, rejected, repeated, settled, and cancelled requests and investigated cases where game state and transaction state did not agree.
Worked across a larger service architecture and learned how the ledger, cache, outbox, replay, reconciliation, and policy layers support the player-facing flow.
Process
C++ REST endpoints execute money movement in PostgreSQL using stable transaction identifiers. Reserve, settle, cancel, transfer, deposit, withdrawal, bonus, locked-funds, bet, and jackpot flows share the same ledger-based model.
Requests can be protected with HMAC-SHA256 signatures, timestamp freshness, nonce replay detection, TLS or mTLS, and rate limiting. A policy-provider boundary runs responsible-gaming and AML checks before money is committed.
The database transaction writes both wallet state and an outbox event. Publishers deliver those events to Kafka, and operators can replay a range, a player's history, or failed deliveries without changing the original ledger entries.
The stateless service can run behind a load balancer. PostgreSQL connection pooling, Redis, Prometheus, Grafana, tracing, Patroni and etcd failover, HAProxy routing, and point-in-time recovery provide a production-oriented operating model.
Capacity
The repository includes a configurable chaos and load harness that reports operations per second and latency percentiles. The figures below deliberately separate a measured development result from infrastructure projections; they are not presented as certified production capacity.
A typical bet uses two durable wallet operations: reserve and settle. On that basis, the storage projection corresponds to roughly 1,000-5,000 completed bet cycles per second before application and workload overhead. Production readiness would still require representative load and soak tests, security review, regulatory validation, and disaster-recovery exercises.
Code samples
These trimmed excerpts come from the shared two-person codebase and illustrate the architecture I worked with. They are included to explain the system, not to claim sole authorship of every subsystem.
Re-publishes stored outbox events and records each success or failure, supporting targeted recovery without rewriting the ledger.
ReplayResult ReplayService::replayByPlayer(
const std::string& operatorId,
int64_t playerId,
std::size_t limit)
{
return replayBatch(
repository.fetchByPlayer(
operatorId, playerId, limit),
false);
}
auto published = producer.publish(
topic, event.aggregate_key, event.payload);
repository.recordReplaySuccess(
event.id, published, repairStatus);Names the supported money movements and failure states so clients and services share one transaction vocabulary.
enum class TxStatus : int32_t {
PENDING = 0,
COMPLETED = 1,
FAILED = 2,
CANCELLED = 3
};
enum class TxType : int16_t {
RESERVE = 1,
SETTLE = 2,
CANCEL = 3,
ADMIN_ADJUST = 4,
DEPOSIT = 5,
WITHDRAW = 6,
BONUS_CREDIT = 7,
TRANSFER = 12
};
enum class ErrorCode : int32_t {
INSUFFICIENT_FUNDS = 2,
DUPLICATE_TRANSACTION = 4,
POLICY_DENIED = 11,
REPLAY_DETECTED = 25
};Folds each immutable ledger row into a SHA-256 chain so a later verification can detect altered history.
h := audit_genesis();
FOR r IN
SELECT * FROM wallet_ledger
WHERE id > v_last_to
ORDER BY id
LIMIT p_max_rows
LOOP
h := sha256(h || audit_canon(r));
END LOOP;
INSERT INTO audit_chain(
batch_no, from_id, to_id,
row_count, prev_hash, chain_hash);Reflection
Money-related state needs stricter guarantees than ordinary game state. Retries, concurrent requests, partial failures, and interrupted rounds must never duplicate a payout, lose a wager, or leave an unexplained balance.
The architecture also has to preserve those guarantees while events, caches, policy checks, replicas, and external services fail independently. That makes observability, replay, reconciliation, and operational testing part of correctness rather than optional extras.
Learning
I learned how atomic operations, idempotency keys, append-only accounting, outbox delivery, replay, and reconciliation work together when a game action changes persistent money state.
I also learned to separate responsibilities clearly: games own rules and presentation, while the wallet owns balances, transaction state, policy gates, and evidence that the result is correct.
Next steps
The next step is to benchmark representative production hardware and workloads, then add full operator or player sharding for horizontal database scale. Before real-money deployment, the system would also need independent security review, regulatory validation, long-running soak tests, and repeated recovery drills.
Explore