Files
OpenFUT/docs/evidence/market-sold-re-2026-08-17/q_tax.out
T
funman300 571c5f9261 docs(market): recover the FIFA17 sold wire contract from CardsDLL (Ghidra)
Task A, static phase. Ghidra 12.1.2 headless via the repo's own pyghidra harness
over CardsDLL_Win64_retail.dll (13,382 functions). Queries and raw decompiler
output committed under docs/evidence/market-sold-re-2026-08-17/.

RECOVERED FROM THE BINARY

1. No sold token, now EXHAUSTIVELY: both vocabularies dumped to their sentinels
   rather than sampled. tradeState is exactly 4 rows; itemState is exactly 12
   (invalid/free/WAITING_FOR_GAME/inGame/forSale/offered/activeBadge/
   activeHomeKit/activeAwayKit/activeBall/activeStadium/active=255). A sold row
   MUST therefore be a combination of existing atoms.

2. What closed does, complete, from the auctionInfo deserializer 0x18013e410:
     IS_GLOW = (tradeState==closed) ? bidState != none
                                    : bidState in {outbid, buyNow}
     INBOX   = bidState in {highest, buyNow}

3. The full record -> Flash map from the publisher 0x1801bf030, superseding the
   partial list. The prize: record +0xbf is published as COINS_AWARDED, fed by the
   coinsProcessed atom 0x2f4. The corpus had recorded that atom's type and noted
   its consumer was never found; it is now traced. DURATION also renders the
   localised FUT_AUCTION_EXPIRED when expires underflows.

4. highest vs buyNow on a closed row is UNDECIDABLE from CardsDLL, by proof: both
   yield IS_GLOW=1/INBOX=1, bit-identical. But bidState is ALSO published verbatim
   as YOURBID alongside STATE and COINS_AWARDED, so the movie does receive the raw
   values - the discrimination exists and lives entirely in unread ActionScript.
   This retires the question as a static target, and it contradicts the
   third-party lore that a seller's sold row is closed+buyNow (the corpus's own
   lifecycle table says closed+highest and assigns buyNow to the buyer).

5. The clear-sold verb EXISTS. Builder 0x1801647c0 emits "/sold" when the tradeId
   field is zero and "/%lld" otherwise, on route base ut/delete/%s/trade, response
   class RS4 FutISRemoveTradeServerResponse. Confirmed by the client's own
   request-name table entry RemoveAllSoldFromTradePile. A BULK clear-sold verb only
   makes sense if sold rows PERSIST in the seller's pile until cleared, which is
   incompatible with our Fix A invariant - so the sold path will require revisiting
   it under live validation.

6. The seller's SOLD counter is real, proven end to end with no inference: the hub
   tradePile sub-deserializer 0x18013ead0 writes atom sold 0x2c9 to +0x1d8, and the
   tile publisher 0x1800b1dc0 renders +0x1d8 as Flash TEXT3 under the localised
   caption FUT_TF_SOLD. Siblings: selling -> +0x1d2 -> FUT_TF_SELLING,
   count -> +0x1d4 -> FUT_UC_ITEMS, plus FUT_TF_WINNING/FUT_TF_OUTBID on the
   Transfer Targets tile. We and the Python oracle both hardcode sold:0, so that
   bucket can never fill.

7. Reusable method: an atom id is the INDEX into the alphabetical atom-name pointer
   table at base 0x1802d2760. Validated 12/12 against the known auctionInfo atoms
   and cross-checked against fifa17-recon/docs/fut_atoms.tsv. Documented gotcha:
   resolve a name by the pointer slot INSIDE the table, never by the first matching
   string in the binary, or you get confident nonsense.

8. An auction-outcome vocabulary exists (auctionSoldBid 0x39, auctionSoldBuyNow
   0x3a, auctionWon*/auctionLost*) but NO deserializer consumes it - every
   candidate function was checked for the value-SKIP/atom-loop signature and none
   qualifies. Server-side or telemetry only; it does not carry sold state here.

TASK B IS UNDECIDABLE FROM THE CLIENT, and this is a proof of absence: no 0.95 or
0.05 constant of either width, no tax/fee/net/proceeds caption, and no fee
arithmetic anywhere. The client never computes or displays a net, so no experiment
against our own server can measure the rounding - whatever we credit is what it
displays, and there is no oracle. Only an original EA-era seller-balance capture
could settle it. The rule stays an explicit CHOICE (floor the fee, so
fee + proceeds == gross exactly) and is now pinned at the requested boundaries
100/101/119/120/149/150/151/199/200 plus 15,000 and i64::MAX.

Settlement NOT promoted. No production process, port or database was touched.
2026-08-18 01:32:02 +00:00

63 lines
2.8 KiB
Plaintext

==============================================================================
== localisation / string keys about tax, fee, net proceeds
==============================================================================
TAX (no printable hits)
tax (no printable hits)
Tax (no printable hits)
FEE 0x18023a3a8 'FEET_ATTEMPTED'
FEE 0x18023a3c0 'FEET_COMPLETED'
_FEE 0x18023a3a7 '_FEET_ATTEMPTED'
_FEE 0x18023a3bf '_FEET_COMPLETED'
fee (no printable hits)
NET 0x180209b1c 'NETOURNAMENT_0'
NET 0x180209b3c 'NETOURNAMENT_1'
NET 0x18020dd10 'NET'
_NET 0x18020dd0f '_NET'
RECEIVE 0x18023933f 'RECEIVER'
RECEIVE 0x180239350 'RECEIVER'
RECEIVE 0x18023983f 'RECEIVER'
RECEIVE 0x18023990d 'RECEIVER'
RECEIVE 0x180239a0b 'RECEIVER'
RECEIVE 0x18023a06f 'RECEIVED'
Receive (no printable hits)
PROCEEDS (no printable hits)
COMMISSION (no printable hits)
EA_TAX (no printable hits)
FUT_TF_ 0x18020a0a8 'FUT_TF_SELLING'
FUT_TF_ 0x18020a0c0 'FUT_TF_SOLD'
FUT_TF_ 0x18020a0d8 'FUT_TF_WINNING'
FUT_TF_ 0x18020a0e8 'FUT_TF_OUTBID'
AFTER_TAX (no printable hits)
earnings (no printable hits)
Earnings (no printable hits)
==============================================================================
== float constants 0.95 / 0.05 / 0.95f / 0.05f
==============================================================================
double 0.95 ABSENT
double 0.05 ABSENT
float 0.95f ABSENT
float 0.05f ABSENT
==============================================================================
== functions using BOTH 95 (or 5) and 100 as immediates — integer fee math
==============================================================================
17 candidate(s)
0x18000b5c0 FUN_18000b5c0 immediates [5, 100]
0x180051cd0 FUN_180051cd0 immediates [5, 20, 100]
0x180057b00 FUN_180057b00 immediates [5, 100]
0x180082c30 FUN_180082c30 immediates [5, 100]
0x180088cb0 FUN_180088cb0 immediates [5, 100]
0x1800a3cb0 FUN_1800a3cb0 immediates [5, 100]
0x1800a47b0 FUN_1800a47b0 immediates [5, 100]
0x1800d5050 FUN_1800d5050 immediates [5, 20, 100]
0x1800d5450 FUN_1800d5450 immediates [5, 20, 100]
0x1801129f0 FUN_1801129f0 immediates [5, 100]
0x18013af30 FUN_18013af30 immediates [5, 20, 100]
0x18013ec10 FUN_18013ec10 immediates [5, 100]
0x18013fe00 FUN_18013fe00 immediates [5, 100]
0x180147070 FUN_180147070 immediates [5, 100]
0x180180ea0 FUN_180180ea0 immediates [5, 100]
0x1801adae0 FUN_1801adae0 immediates [5, 100]
0x1801b9e60 FUN_1801b9e60 immediates [5, 100]