571c5f9261
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.
81 lines
4.3 KiB
Plaintext
81 lines
4.3 KiB
Plaintext
==============================================================================
|
|
== atom ids, resolved against the table range only
|
|
==============================================================================
|
|
sold str=0x18022f524 ptr=0x1802d3da8 ATOM ID = 0x2c9 (713)
|
|
count str=0x180230abc ptr=0x1802d2d40 ATOM ID = 0xbc (188)
|
|
offered str=0x18022a2d8 ptr=0x1802d3688 ATOM ID = 0x1e5 (485)
|
|
selling str=0x18022f460 ptr=0x1802d3d20 ATOM ID = 0x2b8 (696)
|
|
maxAuctionsAllowed str=0x1802318a8 ptr=0x1802d3558 ATOM ID = 0x1bf (447)
|
|
credits str=0x180230af8 ptr=0x1802d2d60 ATOM ID = 0xc0 (192)
|
|
auctionInfo str=0x180230270 ptr=0x1802d2908 ATOM ID = 0x35 (53)
|
|
itemData str=0x180231438 ptr=0x1802d32b8 ATOM ID = 0x16b (363)
|
|
bidState str=0x180230458 ptr=0x1802d2a18 ATOM ID = 0x57 (87)
|
|
tradeState str=0x18022fb48 ptr=0x1802d4108 ATOM ID = 0x335 (821)
|
|
coinsProcessed str=0x18022f768 ptr=0x1802d3f00 ATOM ID = 0x2f4 (756)
|
|
offers str=0x180231ae4 ptr=0x1802d3690 ATOM ID = 0x1e6 (486)
|
|
duplicateItemIdList str=0x180230d18 ptr=0x1802d2ec0 ATOM ID = 0xec (236)
|
|
total str=0x18022fa4c ptr=0x1802d4088 ATOM ID = 0x325 (805)
|
|
|
|
==============================================================================
|
|
== functions that compare against >=3 of the auction-outcome atoms 0x36..0x3c
|
|
==============================================================================
|
|
scanned 13308 functions
|
|
0x180043b90 FUN_180043b90 sees ['0x36', '0x37', '0x38', '0x3c']
|
|
0x1800455b0 FUN_1800455b0 sees ['0x37', '0x38', '0x39']
|
|
0x18006b4c0 FUN_18006b4c0 sees ['0x38', '0x39', '0x3c']
|
|
0x180094ce0 FUN_180094ce0 sees ['0x36', '0x37', '0x38']
|
|
0x180097c70 FUN_180097c70 sees ['0x36', '0x37', '0x38']
|
|
0x1800cabb0 FUN_1800cabb0 sees ['0x38', '0x39', '0x3a']
|
|
0x1800cf3b0 FUN_1800cf3b0 sees ['0x36', '0x38', '0x3a', '0x3c']
|
|
0x1800e6410 FUN_1800e6410 sees ['0x36', '0x37', '0x38', '0x39']
|
|
0x1800ee7c0 FUN_1800ee7c0 sees ['0x36', '0x37', '0x38', '0x39', '0x3a', '0x3c']
|
|
0x1800fb5b0 FUN_1800fb5b0 sees ['0x38', '0x39', '0x3a', '0x3b']
|
|
0x1801071f0 FUN_1801071f0 sees ['0x36', '0x37', '0x38', '0x3a', '0x3b', '0x3c']
|
|
0x18012b370 FUN_18012b370 sees ['0x36', '0x37', '0x38', '0x39', '0x3a', '0x3b', '0x3c']
|
|
0x18012fd40 FUN_18012fd40 sees ['0x36', '0x37', '0x38', '0x3c']
|
|
0x18013fe00 FUN_18013fe00 sees ['0x36', '0x38', '0x3a', '0x3b']
|
|
0x180163770 FUN_180163770 sees ['0x36', '0x38', '0x3a']
|
|
0x180172d00 FUN_180172d00 sees ['0x38', '0x3a', '0x3c']
|
|
|
|
==============================================================================
|
|
== string neighbourhoods: SoldFromTradePile / SOLD / sold
|
|
==============================================================================
|
|
-- SoldFromTradePile
|
|
0x1801efa40 'CardsRequestStaffBonuses'
|
|
0x1801efa60 'CardsGetStaffBonuses'
|
|
0x1801efa78 'WatchListRequest'
|
|
0x1801efa90 'WatchListResultsRequest'
|
|
0x1801efaa8 'AddToWatchList'
|
|
0x1801efab8 'RemoveFromWatchList'
|
|
0x1801efad0 'RemoveFromTradePile'
|
|
0x1801efae8 'RemoveAllSoldFromTradePile'
|
|
0x1801efb08 'GetPhishingQuestion'
|
|
0x1801efb20 'SetPhishingAnswer'
|
|
0x1801efb38 'ValidatePhishingAnswer'
|
|
0x1801efb50 'RetrieveTrustedConsoleList'
|
|
0x1801efb70 'EnableLiveStreaming'
|
|
-- SOLD
|
|
0x18020a040 'D_PRIZES'
|
|
0x18020a050 'EVENT_ID'
|
|
0x18020a05c 'TEXT0'
|
|
0x18020a068 'FUT_UC_SQUAD'
|
|
0x18020a078 'TEXT1'
|
|
0x18020a080 'FUT_TOTAL_AUCTIONS'
|
|
0x18020a098 'FUT_UC_ITEMS'
|
|
0x18020a0a8 'FUT_TF_SELLING'
|
|
0x18020a0b8 'TEXT2'
|
|
0x18020a0c0 'FUT_TF_SOLD'
|
|
0x18020a0cc 'TEXT3'
|
|
0x18020a0d8 'FUT_TF_WINNING'
|
|
0x18020a0e8 'FUT_TF_OUTBID'
|
|
0x18020a0f8 'FUT_GH_TOTAL_PLAYERS_0'
|
|
0x18020a110 'FUT_GH_TOTAL_PLAYERS_1'
|
|
0x18020a128 'FUT_GH_TOP_SCORER'
|
|
-- sold@228bed
|
|
0x180228bc8 'RS4:FutISRemoveTradeServerResponse'
|
|
0x180228bec '/sold'
|
|
0x180228c00 '\\�&'
|
|
0x180228c11 "�'"
|
|
0x180228c28 "X�'"
|
|
0x180228c38 "`�'"
|