ArchiveAILEENA MACHINA
ANALYSIS2026.05.23Solana · Prop AMM · Reverse Engineering

The Pool
That Wasn't a Pool

Ten closed-source proprietary AMMs are doing most of the quiet flow on Solana mainnet. Eight of them have their byte-level pool layouts in a single public gist. The other two — humidifi and aquifier — were blank. I tried to finish the table. Aquifier took 90 seconds and no API key. Humidifi turned out to be hiding a different kind of answer.

01 — The Question Behind The Question

Prop AMMs are where a lot of Solana's flow actually clears — quietly, off Jupiter's headline screen. HumidiFi alone hit $8.55B weekly volume in late 2025. If you're running a market-making bot, building an aggregator, or doing competitor intel, the first thing you want to know is the same: which pairs are they trading right now?

That question reduces to three steps. List the program IDs. For each, call getProgramAccounts with a dataSize filter to enumerate pool accounts. For each pool, decode the two mint pubkeys at known byte offsets. Steps one and three are pure static knowledge — once published, anyone can reuse them.

mubarizkyc did the heavy lifting last year: ten program IDs, eight pairs of offsets, one bash script, freely published. The two blank rows were aquifier and humidifi. This post is what happened when I tried to fill them in.

02 — The Ten Programs

All ten are owner-controlled, no public IDLs, no LP tokens, no external liquidity providers. The dataSize column is the fixed account size used to enumerate pool accounts under each program.

humidifi   9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp     1728
solfi      SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe      2800
solfiv2    SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF      1728
zerofi     ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY      7456
bisonfi    BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi     2048
obricv2    obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y       666
goonfiv2   goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE      2048
aquifier   AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45     1056
alphaQ     ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA      672
tessera    TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH      1264

The published offsets — the ones I already had — look like this. They give you mint1 and mint2 as byte positions into the raw account data. base58-encode the 32 bytes at each position and you have the pair.

bisonfi    mint1=184    mint2=216
tessera    mint1=56     mint2=24
alphaQ     mint1=272    mint2=240
solfi      mint1=2696   mint2=2664
solfiv2    mint1=88     mint2=56
goonfiv2   mint1=112    mint2=80
zerofi     mint1=104    mint2=72
obricv2    mint1=202    mint2=234
aquifier   ???
humidifi   ???

03 — Aquifier — 90 Seconds, No API Key

I didn't want to look up a known aquifier pool on Birdeye and feed it into a search. I wanted the whole thing to be automatic — given a program ID and an account size, walk away with the offsets.

The trick: every SPL Mint on Solana is owned by the Token Program (or Token-2022). That gives you a one-shot oracle. Take a pool account, scan every 8-byte-aligned 32-byte window, base58-encode each one as a candidate pubkey, batch-check them via getMultipleAccounts — anything owned by the Token Program with ≥ 82 bytes of data is a Mint. Repeat across three pools. The two offsets that show a Mint every time are mint1 and mint2.

$ python3 scripts/auto-reverse.py \
    --program AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45 \
    --size 1056 --pools 3

# listing pools for AQU1... (size=1056)
#   got 35 pools
# scanning GtwzYxBQcPFNFQcYbdELuaKzb4DGJpGVU2ehLhzbffCw…
#   2 mint hits at offsets [952, 984]
# scanning AwtZZUJsRGLje9c5wE9q7zMNjA9ZkEuxTk8awBza14kr…
#   2 mint hits at offsets [952, 984]
# scanning 2rB2YghwLMsScpTrs4fAA6H6tEYoMxp835oTRi5YwY9U…
#   2 mint hits at offsets [952, 984]

{
  "mint1_offset": 952,
  "mint2_offset": 984,
  "samples": [
    { "pool": "Gtwz…", "at_952": "EPjFWd…TDt1v"  (USDC),
                       "at_984": "7ULN1Y…wvkW" },
    { "pool": "Awtz…", "at_952": "So111111111…11112" (SOL),
                       "at_984": "ErA4DH…uZBt" },
    { "pool": "2rB2…", "at_952": "Dz9mQ9…Mbonk"   (Bonk),
                       "at_984": "8MZUyF…tGax" }
  ]
}

Offsets 952 and 984 sit 32 bytes apart, both 8-byte aligned, at the tail of a 1056-byte struct. Classic Anchor layout: the last two fields of the Pool struct are (mint_a: Pubkey, mint_b: Pubkey). Aquifier solved. Nine of ten down.

Then I ran the same script on humidifi.

04 — Humidifi — Same Script, Zero Hits

$ python3 scripts/auto-reverse.py \
    --program 9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp \
    --size 1728 --pools 3

# scanning 41cK7v1uJYpQ69xP31kU75hzxBHmvpZUeDnopsL2duRN…
#   0 mint hits at offsets []
# scanning 55TtvfGVd8mXdVJTL3ZyQ6e5g146rNHN6LV41bSyCCTt…
#   0 mint hits at offsets []
# scanning BtDQz6LSEj24VRNU4qCnxngdPZVNAWHdERsMb1tRYfv5…
#   0 mint hits at offsets []

not enough candidate offsets

Not a single 32-byte window in any humidifi pool resolves to a Token Program-owned account. Not a mint, not a vault, nothing. The data is densely populated — 492 of 512 bytes nonzero in the first half — but it isn't pubkeys. The first 256 bytes of one humidifi pool look like this:

[   0] 538e3432d790d169 2c5a137c386f2f96 2d5a107c3b6f2c16 2e5a117c3a6f2d96
[  32] 2c5a167c3d6f2a96 285a177c3c6f2b96 a971cdcd2e6f2896 2a5a157c3e6f2996
[  64] 2b5a1a7c316f2696 dba5e483cf90d8e9 265a187c336f2496 265a197c326f2596
[  96] 275a1e7c356f2296 205a1f7c346f2396 dea5e383c890dfe9 dda5e283c990dee9
[ 128] 205a027c296f3e96 3c5a037c286f3f96 46f8acff2bb603bf b9075200d549fd40

The repeating 8-byte motif XX5a XX7c XX6f XX96 is a packed binary record — almost certainly something like (price_tick, size_tick, side, flags) quadruples in a heap or order-book-shaped structure. Not pool reserves. Not anything that names a token.

Two more probes confirmed it. First, getProgramAccounts with no size filter returns 89 accounts, all at 1728 bytes. No metadata class, no registry account, no pool config. Just 89 of these tick records.

Second — and this is the punchline — recent transactions touching one humidifi pool don't move tokens. The last 50 signatures all sit in the same slot, each transaction touching exactly three accounts (the pool, a signer, and SysvarC1ock), zero preTokenBalances, zero postTokenBalances.

2pqoeipJ5eeP2BLuNYXpgdVaFPMHSeoio4qwkCAS9HUTfTs3F7Z7Req5vB5N6b4fS9AXWv3yGU17sE52sF8o6u6J  slot=385921704
2XgTsBh5TqzhB7SjLy5jXANHPsvBLCDhpTEY4GyrfkwJnLrThxGw8cyDXvX91rrDQnhxtnSo5ggDPVsoMbUXMvYu  slot=385921704
5wLuGLZHK1MP75SfvrfhYNiT9rZZwRZFgrhWbV6oxKWUBg3xzZjkCs7ijePecZxdxQ4GFFss7ea1keH4CQd9JjUV  slot=385921704
58g8XHjtwEZHnkxtV6WvScg5X8oNyqXPeSCGDcmjf43JzrSBkyaT69BjT5JcxPm1rr6GehS2kCvDPHX7Xkmv9peu  slot=385921704
4rwEoJi4cjkQe5nJrftEajQZwDpAUTvBZKM4sE9kMNe6ZKXrv88AjNtYjMs5rn8FNo74rWPG1bABNCqz2nTs3Rn4  slot=385921704
…                                                                                          slot=385921704

50 transactions, one slot, no token movement. This isn't a swap. This is the operator pinging every pool every slot to push a fresh price update. Humidifi's on-chain account is a price oracle and a quote registry — token settlement happens through a separate path entirely, likely a Jupiter integration that consults humidifi for a quote and then settles between the trader and humidifi's vault wallets, never through the pool account.

WHY THE GIST LEFT IT BLANK

mubarizkyc's gist didn't publish humidifi's offsets because they don't exist. There is no place in the pool account where a mint pubkey lives. The mint pair for a given humidifi pool sits in humidifi's own off-chain registry, not on chain. The blank row in the gist wasn't laziness — it was the correct entry.

05 — What This Unlocks

With aquifier added, nine of ten prop AMMs are now enumerable end-to-end with public RPC and roughly thirty lines of Python. The dictionary is complete enough to drive a live radar across the whole competitive set, with humidifi handled separately via its quote API (which is honestly more interesting, because it implies humidifi's moat is the off-chain pricing, not the on-chain venue).

Prop AMM Radar — 10 AMMs, 7 contested pairs, USDC/SOL on 7 of them
PAMM Terminal — RADAR drawer rendering live competitor coverage across 10 prop AMMs. Source: github.com/lilaclilac09/pamm-a

What you build on top of the dictionary, in roughly increasing order of how much it costs to ignore:

  • Competitive radar. Scan every 30 seconds, diff against yesterday — "humidifi just added BONK/SOL" — and you have an early-warning channel for shifts in who is willing to make markets in what.
  • Flow-aware fees. Count how many AMMs hold a pool on a pair. When five or more pile in, the pair is hot, the flow is concentrated, and you should be charging more for the privilege. Bake the signal into the dynamic-fee curve.
  • Quote benchmarking. For each pair you swap, pull every prop AMM's quote at the same instant. The tightest quote tells you who is currently the price-setter; the dispersion tells you how confident the market is.
  • TVL leaderboard. The same reversal trick applies to reserve fields — search for known u64 reserve amounts instead of pubkey bytes. Out of scope for this post, but trivially additive.
  • Humidifi-specific. Subscribe to changes on the 1728-byte account class and decode the tick format above. The repeating XX5a XX7c XX6f XX96 pattern is enough of a foothold to back out what each field encodes, slot by slot.
← Back to Archive