The Poker Solver Market is a marketplace for automated, game-theory-optimal (GTO) poker strategies that play themselves. Creators deploy their solvers to Aptos as Move modules. Players fund the solvers through bonding curves. The solvers play hands onchain as autonomous AutoBots, against human players or against each other. Every hand, every action, every result is recorded onchain. A solver's track record is not a screenshot. It is the chain.
The solvers are built on MonkerSolver, the most widely used and well-tested GTO poker solver in the industry. MonkerSolver handles every meaningful game type: NLHE, PLO, PLO4, tournaments, cash games, heads-up, full ring. That coverage carries over to the marketplace. Any game MonkerSolver can solve, a creator can deploy as a Whop-hosted solver.
The solvers power two venues:
- ›A permissionless onchain poker protocol. Anyone can spin up a table. AutoBots linked to a solver sit down, query the solver's strategy for the current game state, and sample their action using
0x1::randomness. No human at the keyboard, no operator in the middle. The shuffle is ZK-verified mental poker, the deal is provably fair, and the protocol runs until the bot leaves or runs out of chips. - ›Trustless Triad. The same solver primitive plugs into the Trustless Triad game (burn-and-turn poker with a spectator economy). Solvers become players in Triad, and the onchain hand-replay, reputation, and MEV-resistance guarantees carry over directly.
Poker Solver Marketplace
The same architecture as the Indicator Marketplace, applied to poker strategy:
How it works:
- ›Creators deploy poker solvers as Move modules
- ›Solvers encode GTO (Game Theory Optimal) strategies for specific game types
- ›Monte Carlo simulation runs against historical hand databases on Shelby
- ›Certified stats committed onchain at deployment: win rate, ROI, VPIP, PFR, aggression factor
- ›Bonding curve opens for funding, graduates at threshold
- ›Solvers auto-play via AIP-125 scheduled transactions
Solver vs solver. Automated matches between solvers create a verifiable leaderboard. Every hand is recorded. Every decision is onchain. Every result is permanent. A solver that claims 85% win rate in heads-up play either has the onchain record to prove it or it doesn't.
Creator economics: Same model as indicators. Creators earn per profitable session. Chart replay equivalent: full hand replay stored on Shelby with ACE-encrypted hole cards revealed after the hand.
Putting Poker Solvers On-Chain
Online poker has always had a trust problem. When you sit down at a virtual table, you're trusting the platform with everything: the shuffle, the deal, the random number generator, your money. Most of the time this works fine. But "most of the time" isn't good enough when real money is on the line, and the history of online poker is littered with scandals: rigged decks, insider cheating, platforms going dark with player funds.
Blockchain poker projects have been trying to fix this for years. They've tackled the deck (encrypted shuffles), the money (on-chain pots), and the randomness (verifiable RNG). But they all stop at the same place. The game runs on-chain, but the players are still humans clicking buttons in a browser. The strategy (the thing that actually determines who wins) remains entirely off-chain, opaque, and unverifiable.
We went further. We put the solver itself on-chain.
What a Poker Solver Actually Is
Professional poker players don't play by feel. They use solvers: software that computes the mathematically optimal strategy for every possible situation. These solvers run an algorithm called Counterfactual Regret Minimization, iterating millions of times until the strategy converges on a Nash equilibrium. The output is a massive lookup table: for every combination of position, hand strength, and action history, the solver tells you the exact probability distribution over your actions. Fold 23% of the time. Call 45%. Raise 32%.
These strategies are the most valuable intellectual property in poker. MonkerSolver costs hundreds of dollars. PioSolver charges per month. The outputs are guarded as trade secrets. A player's edge is their strategy, and strategies live on private hard drives.
We took that lookup table and stored it in a B+ tree on Aptos.
Why This Works on Aptos (and Nowhere Else)
The idea of storing a 25,000-entry lookup table on a blockchain sounds absurd. On Ethereum, it would cost $25,000 in storage fees. On Solana, $317. On Sui, about $9. On Aptos, it costs four cents.
This isn't a rounding error. Aptos has a data structure called BigOrderedMap (a B+ tree built into the framework) that packs roughly 250 key-value entries into a single storage slot. A solver with 25,000 entries occupies about 100 storage slots instead of 25,000. Each lookup is O(log N), and the cost doesn't scale with table size because only the accessed leaf node is loaded.
Aptos also provides on-chain randomness through a validator VRF. This matters because poker solvers don't output deterministic actions. They output probability distributions. A bot playing game-theory optimal poker needs to sample from those distributions to remain unpredictable. On most chains, you'd need an oracle or a commit-reveal scheme. On Aptos, you call randomness::u64_range(0, 10000) and get an unbiasable random number in the same transaction.
Finally, Aptos has native BLS12-381 pairings and Groth16 proof verification in Move. This is what makes the mental poker protocol possible: card shuffles and reveals are proven with zero-knowledge proofs that verify on-chain in about 100 milliseconds.
The combination of cheap storage, native randomness, and built-in ZK verification doesn't exist on any other chain. We checked.
The Three Layers
The system has three layers, each building on the last.
Layer 1: The Poker Protocol
Eleven Move modules implement a complete Texas Hold'em game with mental poker cryptography. When players sit down at a table, they deposit real APT into the contract. Cards are encrypted using ElGamal on the JubJub curve (a twisted Edwards curve defined over the BLS12-381 scalar field). Each player shuffles the deck in turn, re-encrypting every card with fresh randomness, and submits a Groth16 proof that the shuffle was honest. After all players have shuffled, nobody (not even the shufflers themselves) knows which card is where.
To deal a card to one player, all the other players submit decryption shares. Each share is a scalar multiplication of the player's secret key with the card's encrypted component, accompanied by a ZK proof of correctness. The recipient combines the shares to recover the plaintext card. Nobody else can decrypt it. There's no dealer, no MPC network, no trusted third party.
The hand evaluator runs entirely on-chain in Move. It recognizes all nine poker hand categories (high card through straight flush) with correct kicker comparison. For seven-card evaluation, it iterates all 21 five-card combinations and keeps the best. Side pots are handled through a multi-level allocation algorithm that correctly distributes chips when players are all-in for different amounts.
We tested this with a 20-hand tournament on Aptos devnet. Every hand completed successfully. The evaluator correctly identified two pair beating high card, splits when both players paired the board, and proper blind rotation between hands.
Layer 2: The On-Chain Solver
A solver is stored as four separate B+ trees, one per street (preflop, flop, turn, river). Separating by street eliminates contention between different phases of the game and improves cache locality: a preflop lookup never loads river data.
Each entry in the tree maps a game state to an action distribution. The game state is encoded as a composite key: the player's seat position, a compact encoding of the action history (two bits per action, packed into a 32-bit integer), and a hand strength bucket. The action distribution is three unsigned 16-bit integers representing fold, call, and raise frequencies in basis points (values from 0 to 10,000 that must sum to exactly 10,000).
The bucket design deserves explanation. A naive solver would store one entry per exact hand: 1,326 possible two-card combinations times hundreds of action sequences per street. That's millions of entries. Instead, we use equity-based bucketing: hands with similar equity against random opponent ranges are grouped into the same bucket. With 200 buckets per street, a three-street solver (flop through river) has about 8,400 entries total. This compresses the strategy by 20,000x while preserving the critical information: how strong is my hand relative to the board, and what should I do about it.
We generated a real GTO strategy using our monker-clone CFR solver, a Rust reimplementation of MonkerSolver, validated against the original Java code with 129 out of 129 exact matches on ground truth values. The solver ran 50,000 DCFR iterations on a heads-up river spot, producing 40.7 million raw entries. We downsampled to 200 buckets and uploaded 8,400 entries to Aptos devnet in 168 transactions. Total gas cost: $0.05. Storage cost: about a penny.
You can query the solver right now on devnet. Ask it what to do on the river with pocket aces (bucket 168): fold 0%, call 33%, raise 67%. Ask about a marginal hand (bucket 100): fold 0%, call 33%, raise 67%. Ask about trash (bucket 10): fold 50%, call 40%, raise 10%. These numbers came from a real CFR solver. They live on-chain. Anyone can read them.
Layer 3: The Autonomous Bot
An AutoBot is an Aptos Object with its own address and a stored reference to a solver. The key function is play_turn(), which anyone can call:
The bot reads the current game state through view functions: what phase the game is in, whose turn it is, the pot size, the current bet. It determines which street it's on, encodes the action history into the compact 32-bit format, and queries the solver's BigOrderedMap for the corresponding strategy node. Then it calls Aptos on-chain randomness to sample from the probability distribution and executes the chosen action (fold, call, or raise) by signing a transaction with its own ExtendRef-derived signer.
We tested this on devnet. The bot was at a table, it was its turn preflop, and it held pocket aces (bucket 168). The solver returned fold 0%, call 20%, raise 80%. The on-chain randomness rolled in favor of raise. The bot raised. Transaction hash 0xc1ba618b..., visible on the Aptos explorer. An autonomous on-chain poker bot making a game-theory optimal decision, with no server, no human, no off-chain computation.
The "anyone can crank" design has interesting properties. Bots can trigger each other, creating self-sustaining games. A simple cron job can keep tables running indefinitely. And because cranking is permissionless, there's a natural incentive for infrastructure operators: the cost to crank is negligible ($0.0013 per decision), and the protocol could add tip mechanisms to reward crankers.
The Economics
We measured every operation on Aptos devnet. Here are the real numbers:
A single betting action (fold, call, or raise) costs 82-83 gas, which works out to about eight-hundredths of a cent. The most expensive per-hand operation is the showdown evaluation at 1,703 gas, about 1.7 cents. A complete hand from deal through showdown, with all four streets of betting, costs 3,818 gas total, just under four cents at $10 per APT.
Deploying a solver is mostly a one-time cost. Creating the solver object costs about a dime. Uploading entries costs roughly 68 gas per entry, or about $0.05 for 8,400 entries. The solver lives on-chain indefinitely: no hosting costs, no server bills, no infrastructure to maintain. It's just data in a B+ tree, queryable by anyone, forever.
Running a bot at 100 hands per day costs $3.82 per day in gas. At 1,000 hands per day, $38. The economics are dominated by the strategy quality, not the infrastructure cost. A bot with a 2% edge over its opponents will be profitable at any reasonable volume.
From Solver to On-Chain Bot
The pipeline from "I have a poker insight" to "my bot is playing on-chain" has four steps.
First, you solve. The monker-clone CFR engine runs DCFR iterations on a specified game tree: board texture, stack depth, number of players, bet sizes. The solver outputs a strategy file with probability distributions for every decision point. A river solve with 50,000 iterations takes about five minutes on a modern machine. A full multi-street solve takes longer but produces a complete strategy.
Second, you downsample. The raw solver output has millions of entries with fine-grained isomorphism-based hand buckets. The downsampling tool compresses these into 200 equity-based buckets per street, averaging frequencies within each bin. This reduces 40 million entries to about 8,000 without meaningful loss of strategic fidelity.
Third, you upload. The TypeScript upload tool reads the JSON, batches entries 50 per transaction, and writes them to the solver's BigOrderedMap on-chain. For 8,400 entries, this takes 168 transactions and costs about $0.05 in gas.
Fourth, you deploy a bot. Create an AutoBot object linked to the solver, join a table with an APT buy-in, and the bot is live. Anyone can crank play_turn() to make it act. The bot plays game-theory optimal poker autonomously, sampling from mixed strategies with on-chain randomness, until it leaves the table or runs out of chips.
What We've Verified
Every claim in this document is backed by on-chain transactions or passing tests.
The poker protocol has 20 Move unit and integration tests covering hand evaluation (all nine categories, kicker comparison, side pots), game flow (table creation, check-in, blind posting, street advancement, showdown resolution), and betting mechanics (fold, call, raise, pot size tracking). All 20 pass.
The monker-clone solver has 242 Rust tests, including 129 ground truth values that match MonkerSolver's original Java code exactly: discretization formulas, canonical board keys, hand keys, equity calculations, and suit isomorphism mappings. The solver supports NLHE and PLO4, heads-up and multiway, with configurable bet sizes and DCFR discounting.
The ZK proof pipeline generates and verifies Groth16 proofs for card decryption in 5 seconds (generation) and 48 milliseconds (verification). Both Circom circuits compile cleanly under BLS12-381.
On Aptos devnet, we deployed all 11 modules, played a 20-hand tournament between two players, uploaded 8,000 GTO strategy entries to an on-chain solver, created an AutoBot that raised with pocket aces using a solver query, and measured gas costs for every operation. Everything worked.
The module address, table address, solver address, and bot address are all live on devnet and can be independently verified through the Aptos explorer.
ZK Circuit Specifications
The mental poker protocol uses two Groth16 circuits on BLS12-381:
Shuffle circuit: 356,252 R1CS constraints. Public signals: 418 field elements (208 output deck cards + 2 aggregate public key components + 208 input deck cards). Each player shuffles and re-encrypts the entire 52-card deck. Proof generation: ~30 seconds on consumer hardware. Proof size: 384 bytes (G1 + G2 + G1).
Decryption circuit: 172 R1CS constraints per card. Proves knowledge of a decryption share share_i = sk_i * c1 without revealing the secret key. Proof generation: ~5 seconds. Verification on Aptos: 48ms (three BLS12-381 pairings).
Both circuits compile under Circom targeting BLS12-381 and verify via the aptos_poker::groth16 module using native Move pairing operations.
Bet Tree Configuration
MonkerSolver's internal unit system tracks all chips in milliBB (thousandths of a big blind). 1 BB = 2,000 internal units. A 100 BB stack = 100,000 units.
14 action IDs:
| ID | Action | Sizing |
|---|---|---|
| 0 | Fold | . |
| 1 | Call | Match current bet |
| 2 | Pot | Increment = total pot |
| 3 | All-in | Entire remaining stack |
| 4 | Half-pot | Increment = pot * 0.5 |
| 5 | Min-bet | Minimum legal raise |
| 7 | Quarter-pot | Increment = pot * 0.25 |
| 8 | Double-pot | Increment = pot * 2.0 |
| 9 | Three-quarter-pot | Increment = pot * 0.75 |
| 11+n | Fixed BB amount | n big blinds |
| 40000+n | Percentage of pot | n/1000 of pot |
Pot calculation variants: total_pot() for No Limit (sum of all contributions), effective_pot() for Pot Limit (excludes unmatched portion of the largest bet), capped_pot() for capped games.
SPR-based action filters: Five configurable filter slots control which actions appear in the tree based on stack-to-pot ratio. Default: ip:spr<6.5 enables preflop all-in. If two action IDs produce the same chip state, only one is kept.
DCFR Solver Internals
Discount formula: discount = 10000 / (total_nodes / num_info_sets). This is node-density-based, distinct from the standard Brown and Sandholm alpha/beta/gamma formulation.
Equity scaling: All equity values stored as integers scaled by 2,147,483,647 (i32 max). River equity is computed over 42,783 unique 5-card boards multiplied by C(47,2) = 1,081 opponent hands per board.
Multi-level bucketing: River uses a single level (equity only). Turn and flop use two levels: N primary buckets by mean equity, M secondary buckets by mean-of-squares as a variance proxy. Total buckets per street = N * M.
Canonical key encoding: Board key = 6 bits per card, sorted and packed. Hand key = suit-adjusted relative to the board, packed as (larger << 6) | smaller.
Convergence: Iterations increment by 8,192 per pass. Strategy change is tracked with an exponential moving average (counters halved when updates exceed 10M). Threading uses independent MCCFR iterations per thread with SplittableRandom.split() and shared regret arrays with per-array synchronization.
.mkr File Format
MonkerSolver save files (.mkr) are standard ZIP archives containing ~23 internal files.
Internal structure: Directory names correspond to action names (CALL, BET, FOLD, POT). Leaf files contain alternating lines of hand name and frequency (0.0 to 1.0), with optional EV values after semicolons.
Hand indexing: NLHE uses 169 canonical hands. PLO uses 16,432+ combos with parenthesized notation.
Storage format: TreeFile implements Serializable with serialVersionUID = 8104339003566162407L. Fields: children (subtree references), index (sparse hand indices), freqs (sparse frequencies), evs, name, cost, parent.
Server protocol: SSL socket to app.monkerware.com:3254. Commands: login (0:email:password), register (3:email:password:country), fetch tree (get). Cost stored in euro cents.
PLO Implementation
PLO requires separate infrastructure from NLHE across the solver pipeline:
Suit encoding: NLHE uses full 2-bit suit per card. PLO turn and river use a 1-bit flag (flush possible or not). PLO flop retains full 2-bit encoding.
16-partition equity maps: PLO uses TShortIntHashMap[16] per board instead of a single map. 4 hole cards with 1 bit each = 2^4 = 16 suit structure partitions. Partition index = canonical_key & 0xF.
Memory: 4 to 8x larger than NLHE due to 16 maps per board and 4-byte entries instead of 2-byte.
PLO evaluation: 60 combinations per hand (C(4,2) * C(5,3)). Lookup tables: 35.7M entries (~67MB) for high evaluation, 563K entries (~1MB) for low.
Board counts: Identical for both formats: 1,755 flop boards, 16,432 turn boards, 42,783 river boards.
Reusable components from NLHE: CFR engine, tree builder, tree arena, exploitability calculator, .mkr parser. PLO requires 6 new modules: plo_isomorphism, plo_equity, plo_river/turn/flop_tables, plo_sampler, plo_combos.
Contract Architecture. Implementation Spec
11 modules, ~3,000 lines of Move. Complete architecture with abbreviated function bodies.
Deployed on Aptos devnet:
- ›Module:
0x27c81cb6... - ›Table:
0xdb90adb7... - ›Solver:
0x4d19005f... - ›AutoBot:
0x94c30550...
Module 1: JubJub Curve
JubJub twisted Edwards curve over BLS12-381 scalar field. Used for ElGamal card encryption in the mental poker protocol.
Module 2: Groth16 Verifier
Generic Groth16 zk-SNARK verification using BLS12-381 pairings. Verifies shuffle proofs and decryption share proofs on-chain.
Module 3: ZK Deck
Mental poker encrypted deck. ElGamal encryption + Groth16 verification. 52 cards, each 128 bytes (two JubJub points: c1, c2).
Module 4: Hand Evaluator
Pure computational evaluation: no lookup tables. 21 five-card combos from 7 cards, base-14 kicker encoding.
Module 5: Chip Stack
Internal accounting. Real APT transfers happen at the table layer.
Module 6: Roster
Player seat management. Pending to Active lifecycle, betting operations.
Module 7: Events
Nine event types for indexing and frontend consumption.
Module 8: Table
Game state machine. 10 phases, 10 view functions, devnet bypass. Real APT transfers via primary_fungible_store on check-in/check-out.
Entry functions:
- ›
create_table. create Object, store Table + TableVault, deposit APT, check_in creator - ›
check_in. validate buy_in range, deposit APT, roster.check_in(), proceed() - ›
check_out. roster.check_out(), withdraw APT refund - ›
shuffle_encrypt_deck. verify sender is turn player, zk_deck::shuffle_encrypt(), proceed() - ›
decrypt_card_shares. determine card indices from phase, zk_deck::add_shares(), mark contributed, proceed() - ›
fold,call,raise. verify turn, execute action, proceed() - ›
claim_timeout. verify timeout exceeded, force-fold current player
State machine (proceed):
- ›AwaitingStart: if enough players, refresh, start ShuffleEncrypt
- ›ShuffleEncrypt: advance turn, if wrapped move to DrawPrivateCards
- ›DrawPrivateCards: if all contributed, post blinds, BetPreFlop
- ›BetPreFlop: advance turn, if wrapped to last_raise, DrawFlopCards
- ›Same pattern for flop, turn, river
- ›BetRiver: if wrapped, ShowDown
- ›ShowDown: if all contributed, decrypt cards, evaluate hands, distribute pot, next hand
View functions (10 total):
- ›
get_phase,get_turn_index,get_num_players,get_pot,get_player_address,get_timeout,get_raises_left,get_max_bet,is_player_folded,is_player_all_in
Devnet bypass (skip ZK proofs for testing):
- ›
devnet_start_hand. skip shuffle/decrypt, post blinds, jump to BetPreFlop - ›
devnet_advance_street. skip draw phase, jump to next betting round - ›
devnet_resolve_showdown. evaluate hands with given cards, distribute pot, prepare next hand
Module 9: Solver
On-chain GTO strategy in per-street B+ trees (BigOrderedMap). Storage: $0.04 for 25K entries. Query: $0.0013 per lookup.
Module 10: AutoBot
Autonomous on-chain poker bot. Anyone can crank play_turn(). Uses ExtendRef to sign table transactions on behalf of the bot.
Module 11: Registry
Bot marketplace: registration, performance tracking, leaderboard.
MonkerSolver Bet Tree Configuration System
Reverse-engineered from the decompiled MonkerSolver source. All field names reference the obfuscated Java class/field names for traceability.
Core Classes
| Obfuscated Name | Role |
|---|---|
c.ck_0 (renamed from c.ck) | GameState, holds pot contributions, stacks, fold mask, street, active player |
c.b | Action enum/constants, action IDs and string parsing |
c.bp_0 (renamed from c.bp) | TreeNode, Swing MutableTreeNode wrapping a GameState |
c.d | Action-inclusion filters, condition expressions that decide which actions appear |
c.ao | Main UI / tree manager, limit-type setting (ao.h), number of players (ao.e) |
c.cl | Global config, rake percent, rake cap, iteration limit, convergence thresholds |
c.an_0 (renamed from c.an) | Stack-size UI, per-player stack entry |
c.q | Game type / abstraction, per-street abstraction config files |
c.l | Utility, bitmask operations for fold tracking |
GameState Fields (ck_0)
| Field | Type | Meaning |
|---|---|---|
e[] | int[] | Chips invested per player (pot contributions). Unit = 1/1000 BB (milliBB). |
b[] | int[] | Stack cap per player. Default 100000 = 100 BB (at 1000 milliBB/BB). |
c | int | Dead money / ante already in pot (not belonging to any player). |
g | int | Fold bitmask. Bit n set = player n has folded. Uses l.d(g, n) to test. |
f | int | "Has-acted" bitmask for the current betting round. |
h | int | Street: 0=preflop, 1=flop, 2=turn, 3=river, >=4=terminal/showdown. |
j | int | Index of the player next to act. |
k | int | Raise count on current street (incremented for each bet/raise action other than all-in). |
d | int | Action ID that led to this state (-1 for root). |
i | ck_0 | Parent state (null for root). |
l (field on class) | int | CFR iteration index (set externally). |
Internal unit system: All chip amounts are in milliBB (thousandths of a big blind):
- ›Small blind =
1000 - ›Big blind =
2000 - ›Default stack =
100000(= 100 BB) - ›Stack sizes entered in the UI as "BB" get multiplied by
2000/2 = 1000internally
Action IDs
From c.b, the action system uses integer IDs:
| ID | Constant | Name | Description |
|---|---|---|---|
| 0 | - | FOLD | Fold |
| 1 | - | CALL/CHECK | Call (or check if already matched) |
| 2 | b.c | POT | Pot-sized bet/raise |
| 3 | b.e | ALL_IN | All-in |
| 4 | b.a | HALF_POT | 1/2 pot bet/raise |
| 5 | b.d | MIN_BET | Minimum bet/raise |
| 6 | - | BET (special) | Fixed-limit bet (used when k < 5) |
| 7 | - | QUARTER_POT | 1/4 pot bet/raise |
| 8 | - | DOUBLE_POT | 2x pot bet/raise |
| 9 | b.b | THREE_QUARTER_POT | 3/4 pot bet/raise |
| 10 | - | AUTO | Auto-select best action (resolved to concrete at tree build) |
| 11+ | - | FIXED_BB(n) | Fixed bet of n big blinds. Actual ID = 11 + n. |
| 40000+ | - | PERCENT_POT(n) | Bet of n% of pot. Actual ID = 40000 + n. |
Parsing from strings (b.a(String)):
| Input string | Resulting ID |
|---|---|
"fold" | 0 |
"call" or "check" | 1 |
"bet" | 6 |
"pot" | 2 |
"min" | 5 |
starts with "all" | 3 |
ends with "%" | 40000 + parseInt(before %) |
ends with "b" | 11 + parseInt(before b) |
"auto" | 10 |
starts with "1/2" | 4 |
starts with "3/4" | 9 |
starts with "1/4" | 7 |
| plain integer | 11 + parseInt (fixed BB amount) |
Bet Sizing Formula
The core sizing function is ck_0.a(int street, int player, int actionId) at lines 178-221. Given a GameState, the bet/raise increment (chips added on top of matching the current bet) is:
How the increment is applied:
For bet/raise actions (action ID >= 2, i.e., not fold/call):
Where effective_stack_cap = b(g, player) is the largest stack of any non-folded opponent (you can never effectively bet more than the biggest remaining opponent stack).
For call (action 1):
For fold (action 0):
Pot Calculation Methods
All operate on the current GameState:
d(), Total pot (used for NL/default sizing):
e(), Effective pot (used for PL limit type):
This is the pot that would exist if all players matched the second-highest bet, i.e., excludes the unmatched portion of the largest bet.
f(), Capped pot (used for capped games):
g(), Pot for rake/sizing depending on limit type:
Limit Types
Stored in ao.h (static field). Checked via ao.w() which returns g.k() (the tree's limit type).
ao.h value | Limit type | ao.w() return | Notes |
|---|---|---|---|
| 0 | No-Limit (Hold'em) | 0 | ao.h(0) returns true = "NL" |
| 1 | Pot-Limit (Omaha) | - | From bq.j stored per tree |
| 2 | Fixed-Limit | - | ao.y() returns true |
| 3 | No-Limit (non-Hold'em) | 0 | ao.h(3) returns true = "NL" |
Effect on All-in inclusion (ck_0.f(3)):
Action Legality (ck_0.f(int actionId))
Determines whether an action is legal at the current game state:
z(), Can this player open/raise? Traces back through the action history to find the last voluntary action. Returns true if no prior bet exists (opening), or the prior street is different (new street = can open), or the prior bettor's contribution exceeds this player's (facing a bet/raise).
R(), Minimum raise size:
Big Blind Constant (ck_0.N())
Recursively walks to the root state, then:
For standard games: SB=1000, BB=2000, so N() returns 2000.
Action-Inclusion Filters (Tree Building)
The c.d class manages condition expressions that control which actions are included. These are parsed from string expressions using the a.k.b() parser.
Five filter slots:
| Slot | Variable | Controls |
|---|---|---|
c.d.c (filter 0) | Pre-flop filter | All-in inclusion |
c.d.d (filter 1) | Flop filter | Call/raise inclusion |
c.d.e (filter 2) | Turn filter | Fold inclusion |
c.d.f (filter 3) | River filter | (unused in main logic) |
c.d.g (filter 4) | Post-action filter | Action-size pruning |
Default filter expressions:
These use SPR (Stack-to-Pot Ratio) conditions:
- ›
ip:spr<X= in-position player's SPR is less than X - ›
!ip:spr<X= out-of-position player's SPR is less than X
Tree node expansion: When expanding a tree node, actions are added based on limit type, filter conditions, and duplicate elimination. Custom bet sizes are configurable via string (e.g., "call,pot,1/2,50%,75%,100%,allin").
Raise Cap
The k field in GameState tracks the raise count on the current street.
- ›Fixed-limit: raise cap is 5 (action 6 legal only when
k < 5) - ›No-limit/Pot-limit: no explicit raise cap, limited only by stack depth
- ›When a bet/raise action (not all-in) is applied:
kis incremented - ›All-in (action 3) does NOT increment the raise counter
Street Transitions
A new street begins when all non-folded players who haven't acted have now acted, and all contributions are matched. On street transition:
- ›
streetincrements by 1 - ›
j(next player) resets to player after dealer - ›
f(acted mask) resets to 0 - ›
k(raise count) resets to 0
When street >= 4: the node is terminal (showdown or all-folded).
Global Configuration Parameters
| Field | Type | Default | Meaning |
|---|---|---|---|
cl.h | int | 12 | Display/UI row height |
cl.i | int | 1000 | Rake cap (in milliBB) |
cl.k | double | 0.0 | Rake percentage (0.0 = no rake) |
cl.m | double | 1e-4 | Convergence threshold |
cl.n | double | 1e-7 | Secondary convergence threshold |
cl.o | double | 0.01 | Minimum strategy weight |
cl.t | int | 6 | Thread count for solving |
Rake calculation:
Stack Size Configuration
- ›Default stack per player:
200000milliBB = 200 BB - ›UI entry converts BB to internal:
parseInt(text) * 2000 / 2=text * 1000 - ›Default stack cap:
100000= 100 BB - ›For preflop roots (street == 0), initial blinds:
- ›2-player:
e[0]=1000(SB),e[1]=2000(BB) - ›3+ players:
e[1]=1000(SB),e[2]=2000(BB)
- ›2-player:
Matching MonkerSolver in Rust. Summary
To replicate MonkerSolver's bet tree exactly, a Rust implementation must:
- 02Use milliBB units (1 BB = 2000 internally for blind posting, 1000 per "BB" of stack).
- 04Implement all action IDs (0-10, 11+n for fixed BB, 40000+n for pot%).
- 06Sizing formula: increment =
pot * fractionfor pot-relative bets, capped bymin(stack, effective_opponent_stack). - 08Minimum raise rule: the raise increment must be >= the last raise increment (difference between two largest contributions).
- 10Raise cap: 5 raises per street in fixed-limit. No cap in NL/PL (naturally bounded by stacks).
- 12Street transitions: when all non-folded players have acted and contributions match (or are all-in), advance the street.
- 14Pot calculation: use
total_pot()for NL,effective_pot()for PL (excludes unmatched portion),capped_pot()for other modes. - 16Action inclusion: governed by SPR-based condition expressions, configurable per preset. Default thresholds are SPR < 6.5-10.5 depending on street and position.
- 18Duplicate elimination: if two different action IDs produce the same chip state, keep only one.
- 20All-in in PL: only offered when the all-in amount does not exceed a pot-sized raise.
MonkerSolver .mkr File Format Specification
Overview
.mkr files are MonkerSolver's saved solution format. They contain pre-solved GTO strategies for specific poker scenarios.
Container: ZIP Archive
- ›.mkr files are ZIP archives containing ~23 compressed files
- ›Each internal file represents a node in the game tree
Internal File Structure
Each file inside the ZIP is a directory tree where:
- ›Directory names = action names (e.g., "CALL", "BET", "FOLD", "POT", "1/2 POT")
- ›Leaf files = CSV-like text files with strategy frequencies
Leaf File Format
- ›Lines alternate: hand name, then frequency/EV value
- ›Frequency is a float (0.0-1.0). probability of taking this action with this hand
- ›EV is optional, separated by semicolon (
;, ASCII 59) - ›If all hands have the same EV, a single EV value is stored
- ›Hands with frequency 0.0 are still listed but may be skipped in compact format
Hand Indexing
- ›NLHE: 169 canonical hands (AA, A2s, A2o, A3s, A3o, ... KK)
- ›Order defined in
TreeFile/Calculate.javahandIndex array - ›Omaha: 16,432+ combos with parenthesized notation
Loading Mechanism
From Server (Cloud ranges):
- 02Client sends
"get"toapp.monkerware.com:3254via SSL socket - 04Server responds with a serialized
TreeFileJava object - 06Client deserializes via
ObjectInputStream.readObject() - 08TreeFile is a recursive structure: each node has children (sub-actions)
From Local Disk:
- 02Files are loaded from the
savedRuns/directory - 04Directory tree is walked recursively
- 06Each file is parsed via
TreeFile.initFreqs(File):- ›Read line pairs: skip odd lines (hand names), parse even lines (freq;ev)
- ›Store non-zero frequencies in sparse format:
index[]+freqs[] - ›Store EVs in
evs[](single value if uniform, per-hand array if varying)
Serialization Format
- ›Java standard
ObjectOutputStreamserialization - ›
TreeFile implements SerializablewithserialVersionUID = 8104339003566162407L - ›Fields serialized:
children,index,freqs,evs,name,cost,parent - ›Buffer size: 131072 bytes (128KB) for write buffering
Server Protocol
- ›Host:
app.monkerware.com - ›Port: 3254
- ›Transport: SSL (SSLSocketFactory)
- ›Commands:
- ›
"0:<email>:<password>"-> Login (response: boolean) - ›
"3:<email>:<password>:<country>"-> Register - ›
"get"-> Fetch tree (response: serialized TreeFile object)
- ›
- ›Output stream:
PrintWriter(UTF-8) - ›Input stream:
ObjectInputStream(buffered, 8192 bytes)
TreeFile Java Class
Key Constants
- ›Euro currency: cost stored in cents, displayed as
"EUR" + cost/100 - ›Stack sizes: parsed from name (e.g., "50bb" -> 100000 chips, where BB=2000)
- ›Player count: parsed from path (e.g., "HU" -> 2, "3 way" -> 3)
- ›Version: MonkerViewer 1.4 (from main window title)
MonkerSolver Extracted Algorithms. Complete Reference
DCFR Discount Formula
From c/g_0.java line 727 and c/bq_0.java:
This is NOT standard Brown & Sandholm alpha/beta/gamma. It's node-density-based:
- ›More info sets relative to nodes -> higher discount (faster forgetting)
- ›Fewer info sets -> lower discount (more memory of early iterations)
Applied during regret accumulation: cumulative_regret *= discount
Three Solver Variants:
| Variant | Class | Storage | Discount | When Used |
|---|---|---|---|---|
| Vanilla CFR | bj_0.java | double[][] | None | Small trees |
| DCFR (main) | bk_0.java | long[][] | 10000/density | Default |
| DCFR (compact) | bl_0.java | long+short | 100/density | Large trees, RAM-constrained |
Selection controlled by cl.t flag (0=double, 5=short, 6=long).
Overflow Protection: When accumulators exceed threshold, values are halved:
Equity Computation
Scaling: All equity stored as int scaled by 2,147,483,647 (i32::MAX).
double_equity = stored_int / 2_147_483_647.0
River Equity (e/b/d.java): For each of 42,783 unique 5-card boards:
- 02Enumerate all C(47,2) = 1,081 opponent hands
- 04Sort by hand rank
- 06Use cumulative counting for equity:
equity = (wins + ties/2) / total_opponents where wins = count of opponents our hand beats - 08Store as
TShortIntHashMap<hand_key, scaled_equity>
Turn Equity (e/b/g.java): Two metrics computed:
- ›Mean equity: average river equity over 46 runouts
- ›Mean of squares: average of (river_equity^2) over 46 runouts
- ›Used for variance estimation: var = E[x^2] - E[x]^2
Flop Equity (e/b/b.java): Same two metrics, averaged over 49 turn x 48 river = 2,352 runouts.
Discretization Function
From e/a.java line 98:
Equal-width bins, assigned top-down. Highest value -> highest bucket.
Multi-Level Bucketing
River: Single-level (e/a/d.java: holdemriver_N.ser):
- ›One feature: equity
- ›N buckets
Turn: Two-level (e/a/f.java: holdemturn_N_M.ser):
- ›Feature 1: mean equity -> N primary buckets
- ›Feature 2: mean-of-squares (variance proxy) -> M secondary buckets per primary
- ›Total buckets: N x M
- ›Secondary ranges are per-primary-bucket (not global)
Flop: Two-level (e/a/b.java: holdemflop_N_M.ser):
- ›Same structure as turn: N x M buckets
Canonical Key Functions
Board Key (e/f.java method c(int[])):
- ›Count suits:
suit_counts[card & 3]++ - ›If any suit has 3+ cards: mark as flush-possible
- ›Encode: 6 bits per card, sorted, packed into int
Hand Key (e/f.java method a(int[], int, int)):
- ›Adjust cards relative to board suits
- ›If card's suit has 3+ on board: different encoding than otherwise
- ›Pack two cards as
(larger << 6) | smaller-> short
PLO/Omaha Evaluation
PLO High (h/a/a/b/a.java):
60 evaluations per hand.
PLO Hi-Lo (h/a/a/b/c.java):
- ›High: same as PLO high, uses 35.7M-entry table
- ›Low: 8-or-better qualifier, uses 563K-entry table
- ›Split: if 3+ community cards share a suit, check low
Table Sizes:
| Table | Entries | Memory |
|---|---|---|
| High hand ranks | 35,783,046 | ~67MB |
| Low hand ranks | 563,108 | ~1MB |
| Rank engine | 32,487,834 | ~124MB |
| PLO high lookup | 7,937 | ~16KB |
Convergence
- ›Iterations increment by 8192 per pass (batch processing)
- ›Strategy change ratio tracked:
changes / total_updates - ›When
total_updates > 10,000,000: both counters halved (exponential moving average) - ›No fixed iteration limit. convergence is manual (user stops) or threshold-based
- ›Shown in UI as "EV drift" metric
Threading Model
- ›Main thread spawns N solver threads via
SplittableRandom.split() - ›Each thread runs independent MCCFR iterations
- ›Shared state: regret arrays (synchronized per-array)
- ›Storage selection per tree size: double (small) -> long (medium) -> short+int (large)
PLO Bucketing Analysis. How MonkerSolver Handles Omaha
Extracted from decompiled MonkerSolver code. This is the key finding that determines the effort needed for full PLO support.
PLO bucketing is a 100% separate system from NLHE
MonkerSolver maintains completely parallel infrastructure for PLO vs NLHE. The file naming tells the story: holdemflop_N_M.ser vs omahaflop_N_M.ser.
Key Differences
Suit Encoding:
- ›NLHE: Full 2-bit suit per card (
card & 3) - ›PLO Turn/River: 1-bit suit flag per card (binary: flush-possible or not)
- ›PLO Flop: 2-bit full suit per card
- ›This means PLO uses DIFFERENT canonical key functions per street
Equity Storage: 16-partitioned maps:
- ›NLHE: Single
TShortIntHashMapper board - ›PLO:
TShortIntHashMap[16]per board. 16 parallel maps indexed by suit pattern - ›The 16 partitions come from 4 hole cards x 1 bit each = 2^4 = 16 suit structures
- ›
bucket_index = canonical_key & 0xFselects the partition
Canonical Keys:
- ›NLHE board key: 5 x 6 bits packed into int, sorted
- ›PLO board key: Same structure (board is still 5 cards)
- ›NLHE hand key: 2 x 6 bits packed into short
- ›PLO hand key: Different. must encode 4 cards with suit reduction
Memory: 4-8x larger than NLHE:
- ›16 maps per board instead of 1
- ›int (4 bytes) per entry instead of short (2 bytes)
- ›Same number of unique boards (suit isomorphism applies equally)
Board Counts (same for NLHE and PLO):
| Street | Unique Boards |
|---|---|
| Flop | 1,755 |
| Turn | 16,432 |
| River | 42,783 |
What This Means for the Implementation
The NLHE equity table infrastructure (river_tables.rs, turn_tables.rs, flop_tables.rs) cannot be directly reused for PLO. The following are needed:
- 02PLO canonical key functions: Different suit encoding per street
- 04PLO equity computation: Uses evaluate_plo4() (60 combos) instead of evaluate_direct (1 combo)
- 0616-partition storage:
HashMap<i32, [HashMap<i16, i32>. 16]>instead ofHashMap<i32, HashMap<i16, i32>> - 08PLO hand key: Encodes 4 cards, not 2
The tree builder, CFR engine, and sampler are game-variant-agnostic. The bucketing and equity layers need PLO-specific implementations.
Implementation Plan
Can Reuse (game-agnostic):
- ›CFR engine (cfr.rs). works with any bucket space
- ›Tree builder (builder.rs). action abstraction is game-independent
- ›Tree arena (arena.rs). flat storage doesn't care about hand type
- ›Exploitability calculator. same structure
- ›.mkr parser. format is game-independent
Need PLO-Specific:
- ›
src/abstraction/plo_isomorphism.rs. PLO suit isomorphism with 4-card hands - ›
src/abstraction/plo_equity.rs. PLO equity using evaluate_plo4() - ›
src/abstraction/plo_river_tables.rs. 16-partition storage - ›
src/abstraction/plo_turn_tables.rs. PLO turn equity averaging - ›
src/abstraction/plo_flop_tables.rs. PLO flop equity averaging - ›
src/solver/plo_sampler.rs. 4-card dealing, PLO evaluation at showdown - ›
src/game/plo_combos.rs. C(52,4)=270,725 combo enumeration
MonkerSolver decompilation, Rust port, and onchain poker implementation with autonomous bots by Max Mohammadi.
Trustless Triad
Provably fair heads-up poker where spectators turn into speculators. A novel burn card mechanic creates an information asymmetry between players and spectators, driving a liquid vault economy on Aptos.
The Whop Opportunity
Whop has over 21M users and a recent $200M investment from Tether to become the world's largest internet market. Historically, every creator commerce platform hits the same ceiling: subscription fatigue, content commoditization, and no structural differentiation from competitors. A course on Whop is functionally identical to a course on Gumroad. A community on Whop competes with Discord and Skool.
The missing category is content where the platform IS the product, where the content can only exist on Whop because the platform's infrastructure makes it possible. Provably fair competitive entertainment with real time financial participation is that category, and Aptos can do it.
Trustless Triad is a heads-up poker product inspired by poker legend Tom Dwan, with a novel burn card mechanic that creates an information asymmetry between players and spectators. Spectators trade on that information through a liquid vault which deepens the pot, sharing the spoils with the winner. The game is provably fair and the market is manipulation proof:
Why Poker, and Why Now
The best poker has always been played in person, between people who know each other deeply. A home game with friends. A private cash game in a back room. A heads-up session between two pros who've played thousands of hands against each other. The reads are sharper, the adjustments more precise, the bluffs more meaningful, because the players have relational context that no solver can replicate. This is when poker is most fun to play and most compelling to watch.
Fair poker, played in person with deep relationships, produces the most enjoyable and most interactive viewing experience. When both players' hole cards are visible to the audience, and the audience understands the history between the players, every bet tells a story. Every call is personal. Every fold carries weight.
The problem is that the capital is limited. A private game or a single tournament has a fixed participant pool, a fixed audience, and a fixed pot. The drama is real but the economics are closed. The only people with financial skin in the game are the two players at the table and maybe a handful of backers in the room.
How do you open that experience to third party spectators all around the world, giving them financial participation in real time, while guaranteeing there is no collusion?
This is the problem Trustless Triad solves. Six Aptos native cryptographic primitives replace every human trust layer: the dealer, the RNG, the stream operator, the market operator, the player's ability to trade on their own game, and the whale watcher's ability to track positions. What remains is the poker, the reads, the adaptation, the drama, broadcast globally with spectators who can participate financially through a manipulation proof prediction market.
Shuffle + burns
Fair deal ordering
No front-running
Trustless stream
Private positions
Player restriction
1. Introduction
1.1 The solver problem
GTO solvers have turned competitive poker into a memorization contest. Software like GTO Wizard, PioSOLVER, and MonkerSolver compute Nash equilibrium strategies for any game tree. The edge has shifted from human skill, reads, adaptation, creativity under uncertainty, to memorization fidelity.
"If tomorrow I played GTO Wizard, barring some huge exploit I find, I'm pretty sure I'm close to drawing dead. If it had no new programming and you burn three or four cards, I'm pretty sure I'm betting on me.", Tom Dwan, RIO Q&A, Feb 2026 [1]
"I don't like solvers. Cheaters plus solvers are literally killing poker.", Tom Dwan, Card Player, Mar 2026 [2]
1.2 The trust problem
Existing online poker platforms require players and spectators to trust a centralized operator for deck integrity, financial settlement, stream integrity, and dispute resolution. In high stakes live poker, particularly Macau's private game ecosystem, trust has been enforced by social mechanisms: reputation, relationships, and implicit consequences [2]. These mechanisms are opaque, unverifiable, and occasionally dangerous.
1.3 Contribution
We introduce a system that: (1) modifies the game tree per hand via cryptographic random card removal, favoring human adaptability over cached solver output; (2) creates an information asymmetry where spectators know more than players, enabling a liquid prediction market; (3) replaces every human trust layer with Aptos native primitives. And (4) settles all obligations, player payouts, spectator returns, platform fees, protocol burns, in a single atomic transaction.
2. Game Design: Burn and Turn
2.1 Format
Standard heads-up no-limit hold'em. Before each hand, k cards are randomly removed from the deck, where k is in {1, 2, 3} determined by on-chain randomness. Neither player observes the burn identities. Players see only the count k. Spectators on the livestream see everything: both hole cards, community cards, and all k burn identities.
2.2 Information architecture
PLAYER
Own 2 hole cards
Community cards
Burn count k
SPECTATOR
All 4 hole cards
Community cards
Burn IDENTITIES B = {b₁ ... bₖ}
The spectator's information set strictly dominates the player's. This is novel, in all existing poker broadcasts, spectators and players share the same card information (time shifted).
The system creates an asymmetric information architecture in which spectators possess strictly more information than the players at the table, a property that has never existed in poker broadcasting. This information asymmetry drives a liquid vault economy where spectators trade binary outcome shares ($0.00-$1.00 USDT) based on solver computed equity derived from revealed burn card data.
2.3 Effect on solver equilibria
GTO solutions assume a 52-card deck. Burns introduce uncertainty over the true composition from the players' perspective:
52
trees for k=1
1,326
trees for k=2
22,100
trees for k=3
23,478
total game trees
Each burn combination produces a distinct equilibrium. The "burn delta" delta measures the equity shift per hand:
| Distribution | delta range | Description |
|---|---|---|
| ~30% of hands | <1% | Noise, burned cards don't interact with either hand |
| ~40% of hands | 1-5% | Material, one burn removes a key out or blocker |
| ~20% of hands | 5-15% | Significant, key card burned, large equity swing |
| ~10% of hands | >15% | Dramatic, multiple critical outs burned |
2.4 Why heads-up
- ›Anti-collusion. Multi-player collusion is structurally impossible with two players. You cannot collude with your only opponent.
- ›Maximizes burn effect. Each player's 2 cards represent a larger fraction of the information space in heads-up, amplifying the impact of burns.
- ›Optimal spectator format. Binary outcome maps directly to the $0.00-$1.00 prediction market. Clear narrative. Maximum drama.
3. Cryptographic Architecture
Six Aptos native primitives. Each eliminates a specific trust dependency.
3.1 Aptos Roll, on-chain randomness AIP-41
The first secure, instant on-chain randomness for any PoS blockchain [3][4]. Uses weighted distributed key generation (wDKG) among validators. Per-transaction randomness that is unpredictable and unbiasable for any adversary controlling <50% of stake.
The novel contribution is the wPVSS (weighted Publicly Verifiable Secret Sharing) scheme. DFINITY uses threshold (shares = validator count, hundreds). Aptos uses weighted (shares proportional to stake, thousands), a qualitatively harder construction.
| Property | Aptos Roll | Chainlink VRF | DFINITY | Drand |
|---|---|---|---|---|
| Native to consensus | Yes | No (oracle) | Yes | No (beacon) |
| Instant (single TX) | Yes | No (~2s) | Yes | No |
| PoS-weighted | Yes (wPVSS) | N/A | No (threshold) | N/A |
| Additional trust | None | Chainlink nodes | None | League of Entropy |
Application to burn selection
Eliminates: The dealer, the RNG operator, any single party with privileged card access.
3.2 Prefix Consensus, multi-proposer fair dealing arXiv:2602.02892
Standard BFT designates a single leader per round. In poker, a single leader could bias the shuffle by selectively censoring randomness contributions. Prefix Consensus [6] eliminates this via a multi-proposer protocol with f-censorship resistance: 3-round async, O(n^2) complexity, ranking demotion for censoring validators.
Eliminates: Single-leader deal manipulation, selective censorship of randomness contributions.
3.3 Encrypted Mempool, fair vault markets Oct 2025
Trade intent (direction, size, target vault) is encrypted before entering the mempool [7]. Validators process without seeing contents. Decryption after block ordering finalized. Batched threshold decryption: O(n) complexity, <20ms per batch.
Three-layer privacy pipeline
Eliminates: MEV extraction, front-running, speed-based manipulation, validator advantage.
3.4 Shelby, trustless stream delivery Shelby Whitepaper
Decentralized CDN built by Aptos Labs + Jump Crypto [8][9]. Stream segments erasure-coded via Clay codes into fragments across global SPs. No single SP holds a complete frame. RPC nodes reconstruct at read time, all spectators receive the complete stream simultaneously. Dual role: trustless video delivery AND permanent solver data storage (Merkle root commitments on Aptos).
Eliminates: Stream operator as information-privileged entity, centralized CDN preview access.
3.5 Three-layer privacy architecture
Layer A: Confidential Assets (AIP-143) Twisted ElGamal
Individual vault balances encrypted using Twisted ElGamal homomorphic encryption [10][11][12]. 64-bit transfers, 128-bit balances. The vault contract updates encrypted balances without decrypting. Pending/actual balance split provides native front-running protection. Auditor key for selective disclosure.
Public: aggregate market data (price, depth, volume), maintained as public counters. Private: individual positions, encrypted, untraceable on block explorer.
Layer B: Invisible Assets ZK circuit UTT / Groth16
Proves in zero knowledge that the trader is NOT a registered player during an active hand [13][14]. References Aptos Keyless credential (AIP-61 [15]). If prover is a registered player, proof is unconstructable. Performance: Groth16 path = 370ms proof, 1ms verification, 0.4 KiB.
Eliminates: Insider trading by players, position sniping, behavioral signal trading.
3.6 Verified Solver Marketplace KZG / Hyperproofs
KZG Polynomial Commitments commit a solver's database as a single curve point [16]. Groth16 proves exploitability bounds without revealing strategy. Hyperproofs [17] enable O(log n) proof updates when databases change. BigOrderedMap (B+ tree) for the 23,478-entry solver index on Aptos [18]. Block-STM parallel lookups across burn combinations.
Eliminates: Unverified solver claims, platform-controlled fee manipulation.
4. The Spectator Economy
4.1 Vault structure
Each player has a vault. Shares trade $0.00-$1.00 USDT. Price = implied win probability. Pays $1.00 if player wins, $0.00 if loses. Always sums to $1.00 across both players.
Currency architecture
| Layer | Currency | Rationale |
|---|---|---|
| Table chips / pot sizes | APT | Brand association, Aptos logo on every chip, every clip |
| Vault deposits / share pricing | USDT | Dollar denominated, no FX risk for spectators |
| Gas / protocol fees | APT | Burns via Proposal 183. Structural deflation |
| Solver subs / stream access | APT | Recurring demand from spectator base |
4.2 The burn card information edge
The spectator's alpha is the gap between solver computed equity (exact, using burn data) and market implied equity (vault share price, subject to noise). Average delta ~ 3-5% provides sufficient alpha to sustain active trading.
Cold start solution: Burns CREATE the information edge. Without burns -> wide estimates -> low confidence -> small deposits. With burns -> precise equity -> high confidence -> large deposits -> liquidity -> more spectators -> more deposits.
4.3 Dynamic fee optimization
| Burn scenario | delta | Fee (30% buffer) | Spectator edge |
|---|---|---|---|
| Noise | 0.5% | 0.35% | +0.15% |
| Material | 4.0% | 2.80% | +1.20% |
| Significant | 12.0% | 8.40% | +3.60% |
| Average hand | 3.5% | 2.45% | +1.05% |
4.4 Solver oracle consensus
Platform ships with a first-party solver (Vision Pro). Third-party solvers displace it as they prove superior accuracy. The market converges toward the best available pricing without any party controlling the oracle. Solver index uses BigOrderedMap (B+ tree), ~3-4 storage reads per lookup across 23,478 entries. Block-STM parallel execution across different burn combinations.
5. The Reputation Ledger
5.1 Atomic settlement
There is no state in which the player has been paid and a counterparty has not. The atomic guarantee is what makes the Reputation Ledger credible.
5.2 IBE: send money to a Twitter handle
Counterparties identified by any OAuth2/OpenID handle: Twitter, email, phone, Google (via Aptos Keyless, AIP-61 [15]), Apple ID. Multiple fallback identities per counterparty. The player publishes an obligation schedule on-chain. Atomic splits route USDT to IBE-encrypted escrow keyed to each identity. Counterparties claim by authenticating. Unclaimed funds after 24+ months auto-refund, the refund is a public on-chain event demonstrating good faith.
5.3 Network effects
Each IBE escrow is a user acquisition mechanism. Counterparty receives notification, authenticates, receives funds, joins the platform. Their own financial relationships settle through the same infrastructure. The obligation graph becomes the distribution graph.
6. Anti-Collusion Properties
| Vector | Elimination |
|---|---|
| Multi-player collusion | Heads-up, cannot collude with sole opponent |
| Dealer collusion (rigged deck) | Aptos Roll + Prefix Consensus [3][6] |
| Market front-running | Encrypted mempool [7] |
| Information leakage (early access) | Shelby erasure coding, simultaneous delivery [8] |
| Insider trading (player trades) | Invisible Assets ZK, proof unconstructable [13] |
| Position sniping | Confidential Assets, encrypted balances [10] |
Ghosting Remaining | Statistical detection: ~20 BB/100 signal, N* ~ 100-150 hands [19] |
7. Revenue Architecture
Players pay zero fees. All revenue derives from the information economy created by the burn mechanic.
| Source | Annual (12 matches) | Currency |
|---|---|---|
| Vault trading fees | $12-36M | USDT |
| Stream access (Shelby) | $2M | APT |
| Solver marketplace | $0.5-2M | APT |
| Prop bet settlement | $2.4-12M | Both |
| Content / courses (Whop) | $5-10M | USDT |
| Data licensing | $0.5-2M | USDT |
| Total | $22-64M |
8. Distribution
Distributed via Whop (21M users). All spectator products, match access, courses, solver overlays, community, are Whop storefronts. The on-ramp is abstracted: spectators on-ramp through Whop via credit card or Apple Pay, converting to APT and USDT automatically. USDT is the vault rail. Aptos is the settlement layer. Neither is visible to the end user.
9. Poker as Technology Catalyst
| Era | Technology | What spectators saw | Result |
|---|---|---|---|
| Pre-1999 | None | Community cards only | Unwatchable |
| 1999-2003 | Hole cameras | Players' hole cards | Spectator sport |
| 2003-2011 | Online platforms | Anyone could play | Participation sport |
| 2014-2020 | Twitch | Real time + commentary | Interactive entertainment |
| 2021-now | TikTok / Shorts | Highlights, viral | Content engine |
| 2026 | Burns + Vaults | More than players + financial skin | A market |
Content Rewards flywheel
Every match produces auto-generated clips via AI clipping engine on Shelby. Whop creators share clips and earn rewards proportional to vault deposits driven, tracked by PropWhop (on-chain attribution).
Burn Reveal Vault Swing Scoreboard Update Record Pot
10. Second and Third-Order Consequences
10.1 Provably fair dealing Fairness
1st order: Every hand cryptographically verifiable. Anyone can audit any hand.
2nd order for Whop: Provable fairness eliminates regulatory/reputational risk of hosting gambling content. Whop becomes the first platform with a cryptographic fairness guarantee, opening a content category others cannot safely offer.
2nd order for Aptos: Validates Aptos Roll for high-stakes financial applications (insurance, lotteries, compliance). Every institution evaluating on-chain randomness references Trustless Triad as the stress test.
3rd order: Industry standard shifts from "trust us" to "verify the proof." Aptos becomes default for legally-defensible randomness.
10.2 Collusion elimination Template
1st order: Six vectors eliminated architecturally.
2nd order for Whop: Anti-collusion table becomes a reusable template for any competitive format on Whop. Trust transfers from individual creator to architecture.
2nd order for Aptos: Proves primitive COMPOSITION is the moat. Encrypted mempool without Confidential Assets leaves positions exposed. Aptos Roll without Prefix Consensus allows leader manipulation. No other chain composes all six.
3rd order: Reference architecture for "how to make any competitive system trustless", auctions, procurement, voting, grading.
10.3 The Reputation Ledger Moat
1st order: Atomic splits to counterparties via IBE escrow.
2nd order for Whop: Transforms Whop into a financial settlement layer. Any creator publishes revenue-sharing that settles atomically. IBE lets creators pay collaborators identified only by a Twitter handle. Every unsettled relationship becomes a reason to join Whop.
2nd order for Aptos: IBE escrow becomes the most intuitive crypto onramp ever: authenticate with Google -> receive money -> you're on Aptos. At scale across 21M Whop users, this is Aptos's largest retail onramp.
3rd order: Financial obligation graph expands from poker to music rights, real estate commissions, insurance claims. Every on-chain relationship is a switching cost. The Reputation Ledger is the moat for both Whop and Aptos.
10.4 Positive-sum spectator economy New Primitive
1st order: Revenue from information market, not extraction. Players pay zero.
2nd order for Whop: Vault model applies to any competitive content, esports, cooking, debates. Subscriptions monetize loyalty. Tips monetize generosity. Vaults monetize conviction.
2nd order for Aptos: Largest real time on-chain prediction market. Vault tokens as new DeFi asset class, collateral, DEX-tradable, structured products.
3rd order: Any format where audience has computable information advantage can support a vault economy. The architecture generalizes.
10.5 Technical depth as ecosystem proof Reference Impl.
1st order: Most complex application ever built on Aptos.
2nd order: Battle-tested integration patterns for developers. Unfakeable benchmark no competing chain can replicate. Shelby validated for consumer-facing, financially-critical content.
3rd order: Proves cryptographic research ships as consumer product. The distance from paper to phone collapses. The argument for Aptos Foundation grants, academic collaboration, and institutional confidence.
11. Conclusion
Trustless Triad demonstrates that six Aptos native cryptographic primitives can compose into a complete application that eliminates every human trust dependency in a high-stakes financial entertainment product. The burn card mechanic transforms heads-up poker into a novel spectator economy by creating an information asymmetry that has never existed in competitive entertainment.
Poker has driven every major internet platform adoption cycle for 25 years. Hole cameras made it watchable. PokerStars made it playable. Twitch made it streamable. TikTok made it clippable. Trustless Triad makes it investable.
The system cannot be built on any other chain. It requires native instant randomness (not an oracle callback), parallel execution for concurrent solver lookups (Block-STM), sub-second finality, weighted PoS secret sharing (not threshold), and protocol-level asset confidentiality. The absence of any one property breaks the architecture.
References
[1] T. Dwan, "Run It One Elite Q&A," GipsyTeam, Feb 2026. [2] M. Kaplan, "Elusive Poker Pro Tom Dwan," Card Player, Mar 2026. [3] AIP-41: Move APIs for Public Randomness. [4] A. Tomescu & Z. Xiang, "Roll with Move," Aptos Labs, 2024. [5] Aptos Randomness API docs. [6] Z. Xiang, A. Tonkikh, A. Spiegelman, "Prefix Consensus," arXiv:2602.02892, Feb 2026. [7] R. Fernando et al., "Encrypted Mempool," Aptos Labs, Oct 2025. [8] O. Goren et al., "Shelby," 2025. [9] A. Crystal, O. Goren, S. Kominers, "Shelby Incentive Analysis," 2025. [10] AIP-143: Confidential Assets. [11] S. Xiao, "Confidential APT," Mar 2026. [12] A. Tomescu, "CA: A Researcher's Take," Mar 2026. [13] A. Tomescu et al., "UTT: Decentralized Ecash with Accountable Privacy," 2022. [14] Aptos Labs, "Invisible Assets," 2024. [15] AIP-61: Keyless Accounts. [16] A. Tomescu, "Groth16." [17] A. Tomescu et al., "Hyperproofs," USENIX Security 2022. [18] "BigOrderedMap," Aptos Labs, Mar 2025. [19] GTO deviation analysis est.
Trustless Triad, Built on Aptos, Streamed on Shelby, Distributed via Whop
Onchain Poker App
The poker protocol is three layers:
- 02
The onchain poker game. 11 Move modules handling table creation, player management, ZK-verified card shuffling and dealing, hand evaluation, betting, and settlement. All state lives onchain. Cards are encrypted via mental poker (ElGamal on JubJub + Groth16 proofs). No trusted dealer.
- 04
The solver marketplace. GTO strategy data is computed offchain using a MonkerSolver clone (242 tests, NLHE + PLO4), downsampled from millions of entries to ~8,000 equity buckets, and uploaded to onchain B+ trees (BigOrderedMap). Creators deploy solvers, players fund them through bonding curves, and performance is tracked with verifiable results. Storage: $0.04 per 25K entries. Query: $0.0013 per lookup.
- 06
Autonomous bots. An AutoBot joins a table, links to an onchain solver, and plays hands by querying the solver's B+ tree and sampling actions with onchain randomness. Anyone can crank
play_turn(). The bot plays GTO poker autonomously until it leaves or runs out of chips.
The privacy stack makes it possible. Traditional onchain poker exposes hand information to validators and observers. Aptos's privacy primitives solve this at the protocol level:
- ›Confidential Assets hide bet sizes from other players at the table
- ›UTT hides player identity and prevents wallet-based profiling
- ›ACE encrypts hand data with time-locked reveal after each round
- ›Encrypted mempool prevents validators from seeing actions before commitment
The randomness is native. 0x1::randomness provides onchain VRF for card dealing. No external oracle. No trust assumption. The shuffle is provably fair and verifiable by any observer after the hand completes.
Game Details
Table structure:
- ›Texas Hold'em (No Limit, Pot Limit, Fixed Limit)
- ›Sit and Go tournaments
- ›Cash game tables with variable stakes
- ›Heads-up challenge mode (solver vs solver)
Privacy per phase:
- ›Pre-flop: Hole cards ACE-encrypted. Only the player can see their own cards.
- ›Flop/Turn/River: Community cards revealed via time-locked ACE. All players see them simultaneously.
- ›Betting: Bet amounts use CA. Other players see that a bet was made but not the exact size until the round completes.
- ›Showdown: Losing hands can remain hidden (mucking). Winning hands are revealed with ZK proof of validity.
Randomness: Card dealing uses 0x1::randomness::permutation with a commit-reveal pattern. The deck is shuffled onchain. No party can predict or influence the deal.
Social Layer
Everything from Whop's social features applies:
- ›.whop names as player identities with permanent poker track records
- ›Chart replay equivalent: Full hand replays on Shelby with commentary
- ›PnL cards for poker sessions with Reclaim-attested results
- ›Copy play: Follow a solver's decisions in real time (per-second CA stream)
- ›Content Rewards: Hand replay clips earn creator revenue when viewed
- ›Explorer leaderboard: Poker-specific rankings by stake level, game type, and session count
Whop Appstore Integration
The poker platform is a Whop Appstore web app, like Content Rewards. Creators attach it to their Whop page alongside their trading products. A signal caller who also plays poker can have both their trading track record and poker track record under the same .whop name.
Solver creators deploy their strategy, set a funding target, and open a bonding curve. Players fund solvers they believe in. When the curve graduates, the solver goes live and starts auto-playing. Creators earn per profitable session, and hand replay clips posted through Content Rewards campaigns earn additional revenue based on viewership.
Technical Requirements
Everything needed is already in the Aptos Move VM:
- ›
0x1::randomnessfor card dealing - ›AIP-143 (CA) for hidden bet amounts
- ›ACE for encrypted hole cards with time-locked reveal
- ›AIP-125 for automated solver execution
- ›Block-STM for parallel table execution (multiple tables in the same block)
- ›Shelby for hand history storage and replay clips
- ›Reclaim zkTLS for cross-platform poker stats verification
The encrypted mempool (pending governance) would add pre-action privacy, preventing validators from seeing fold/call/raise decisions before commitment.