Files
OpenFUT/docs/evidence/market-sold-re-2026-08-17/q_sold_1.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

309 lines
10 KiB
Plaintext

==============================================================================
== auctionInfo deserializer 0x18013e410 — full decompilation
==============================================================================
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
undefined4 FUN_18013e410(undefined8 param_1,longlong param_2,undefined8 param_3)
{
longlong *plVar1;
undefined1 *puVar2;
undefined1 *puVar3;
undefined ***pppuVar4;
undefined ***pppuVar5;
char cVar6;
int iVar7;
int iVar8;
undefined8 uVar9;
longlong *plVar10;
undefined4 uVar11;
undefined1 auStack_138 [32];
undefined **local_118;
undefined8 local_110;
longlong local_108;
undefined8 local_100;
undefined **local_f8;
undefined1 local_f0 [32];
undefined1 local_d0;
longlong *local_c8;
undefined8 local_c0;
undefined1 local_b0 [36];
undefined4 local_8c;
undefined4 local_88;
undefined4 local_84;
undefined4 local_80;
int local_70;
undefined8 local_68;
undefined1 local_58 [8];
longlong local_50;
longlong *local_48;
int local_40;
undefined1 local_3c;
undefined1 local_3b;
undefined1 local_3a;
undefined1 local_39;
ulonglong local_38;
local_100 = 0xfffffffffffffffe;
local_38 = DAT_1802db6e0 ^ (ulonglong)auStack_138;
iVar8 = 0x38c;
FUN_18010f5f0(&local_f8);
iVar7 = FUN_1801c7f10(param_2);
while (iVar7 != 10) {
if (*(int *)(param_2 + 0xd0) == 0xb) {
iVar8 = FUN_180180d00(*(undefined8 *)(param_2 + 0xf8));
iVar7 = FUN_1801c7f10(param_2);
if (iVar7 != 6) goto LAB_18013e498;
}
else {
LAB_18013e498:
if (iVar8 < 0x2b8) {
if (iVar8 == 0x2b7) {
uVar9 = FUN_1801c7aa0(param_2);
FUN_180008120(local_b0,uVar9,0x1e);
}
else if (iVar8 < 0x117) {
if (iVar8 == 0x116) {
local_68 = FUN_1801c79d0(param_2);
}
else if (iVar8 == 0x57) {
uVar9 = FUN_1801c7aa0(param_2);
local_40 = FUN_180166380(uVar9);
}
else if (iVar8 == 0x65) {
uVar9 = FUN_1801c79d0(param_2);
local_88 = FUN_1800d7b30(uVar9);
}
else {
if (iVar8 != 0xc1) goto LAB_18013e59b;
uVar9 = FUN_1801c79d0(param_2);
local_80 = FUN_1800d7b30(uVar9);
}
}
else if (iVar8 == 0x16b) {
FUN_18013fe00(local_58,param_2,0);
}
else if (iVar8 == 0x2b6) {
uVar9 = FUN_1801c79d0(param_2);
local_8c = FUN_1800d7b30(uVar9);
}
else {
LAB_18013e59b:
FUN_180135ff0(iVar8,param_2);
}
}
else if (iVar8 == 0x2e6) {
uVar9 = FUN_1801c79d0(param_2);
local_84 = FUN_1800d7b30(uVar9);
}
else if (iVar8 == 0x2f4) {
uVar9 = FUN_1801c79d0(param_2);
local_39 = FUN_1800d7b50(uVar9);
}
else if (iVar8 == 0x331) {
local_c0 = FUN_1801c79d0(param_2);
}
else if (iVar8 == 0x335) {
uVar9 = FUN_1801c7aa0(param_2);
local_70 = FUN_180166bd0(uVar9);
}
else {
if (iVar8 != 0x380) goto LAB_18013e59b;
cVar6 = FUN_1801c7620(param_2);
local_3c = cVar6 != '\0';
}
}
iVar7 = FUN_1801c7f10(param_2);
}
if (local_70 == 4) {
local_3b = local_40 != 0;
}
else {
local_3b = (local_40 - 1U & 0xfffffffd) == 0;
}
local_3a = local_40 - 2U < 2;
local_d0 = 1;
FUN_180007f60(local_f0,param_3,1);
plVar10 = (longlong *)FUN_18011a830();
if (local_48 == (longlong *)0x0) {
local_110 = 0;
local_118 = &PTR_LAB_180214de8;
local_108 = 0;
(**(code **)(*plVar10 + 0xa00))(plVar10,&local_118,local_c0);
if (local_108 == 0) {
uVar11 = 1;
goto LAB_18013e795;
}
if ((local_58 != (undefined1 *)(local_108 + 0xa0)) &&
(plVar1 = *(longlong **)(local_108 + 0xb0), local_48 != plVar1)) {
if (local_48 != (longlong *)0x0) {
puVar2 = (undefined1 *)*local_48;
puVar3 = *(undefined1 **)(puVar2 + 8);
if (local_58 == puVar2) {
*local_48 = (longlong)puVar3;
}
else {
for (; puVar3 != (undefined1 *)0x0; puVar3 = *(undefined1 **)(puVar3 + 8)) {
if (puVar3 == local_58) {
*(undefined8 *)(puVar2 + 8) = *(undefined8 *)(puVar3 + 8);
break;
}
puVar2 = puVar3;
}
}
}
local_48 = plVar1;
if (plVar1 != (longlong *)0x0) {
local_50 = *plVar1;
*plVar1 = (longlong)local_58;
}
}
local_118 = &PTR_LAB_180214de8;
if (local_108 != 0) {
pppuVar4 = *(undefined ****)(local_108 + 0x30);
pppuVar5 = (undefined ***)pppuVar4[1];
if (&local_118 == pppuVar4) {
*(undefined ****)(local_108 + 0x30) = pppuVar5;
}
else {
for (; pppuVar5 != (undefined ***)0x0; pppuVar5 = (undefined ***)pppuVar5[1]) {
if (pppuVar5 == &local_118) {
pppuVar4[1] = pppuVar5[1];
break;
}
pppuVar4 = pppuVar5;
}
}
}
}
(**(code **)(*plVar10 + 0x9f8))(plVar10,param_1,&local_f8);
uVar11 = 10;
LAB_18013e795:
FUN_18000a3c0(local_58);
local_f8 = &PTR_FUN_18021b520;
while (local_c8 != (longlong *)0x0) {
plVar10 = (longlong *)local_c8[1];
(**(code **)(*local_c8 + 8))();
local_c8 = plVar10;
}
return uVar11;
}
==============================================================================
== Flash publisher 0x1801bf030 — full decompilation
==============================================================================
void FUN_1801bf030(longlong *param_1,longlong param_2,undefined8 param_3,undefined8 param_4)
{
longlong lVar1;
longlong *plVar2;
longlong lVar3;
int iVar4;
undefined4 local_res8 [2];
undefined4 local_res10 [2];
undefined4 local_res18 [2];
undefined4 local_res20 [2];
undefined1 *local_b0;
undefined1 *local_a8;
undefined1 *local_a0;
longlong *local_98;
undefined4 local_90;
char *local_88;
longlong local_80 [2];
longlong local_70;
longlong *local_68;
(**(code **)(*param_1 + 8))
(param_1,(*(longlong *)(param_2 + 0x38) - *(longlong *)(param_2 + 0x30)) / 0x18,*param_1
,param_4,0xfffffffffffffffe);
iVar4 = 0;
lVar3 = *(longlong *)(param_2 + 0x30);
if (lVar3 != *(longlong *)(param_2 + 0x38)) {
do {
lVar1 = *(longlong *)(lVar3 + 0x10);
FUN_1800d8fa0(*(undefined8 *)(lVar1 + 0x38),local_res10,local_res8);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TRADEID_LOWER",local_res8[0]);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TRADEID_UPPER",local_res10[0]);
FUN_1801a9200(local_80,*(undefined4 *)(lVar1 + 0x6c));
local_98 = (longlong *)FUN_1800d7370();
local_90 = 1;
local_88 = "FUT String";
local_b0 = &DAT_1802e13a0;
local_a8 = &DAT_1802e13a0;
local_a0 = &DAT_1802e13a1;
if (*(longlong *)(lVar1 + 0x90) - 1U < 0x20f5800) {
FUN_1801c2e50(&local_b0);
}
else {
plVar2 = (longlong *)FUN_18019e320();
(**(code **)(*plVar2 + 400))(plVar2,&local_b0,"FUT_AUCTION_EXPIRED",1);
}
(**(code **)(*param_1 + 0x30))(param_1,iVar4,"DURATION",local_b0);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"TIME_REMAINING",*(undefined4 *)(lVar1 + 0x90));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"MIN_CREDITS",*(undefined4 *)(lVar1 + 0x78));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"MAX_CREDITS",*(undefined4 *)(lVar1 + 0x70));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"RESERVEDPRICE",*(undefined4 *)(lVar1 + 0x74));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"YOURBID",*(undefined4 *)(lVar1 + 0xb8));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"STATE",*(undefined4 *)(lVar1 + 0x88));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"COINS_AWARDED",*(undefined1 *)(lVar1 + 0xbf));
FUN_1800d8fa0(*(undefined8 *)(*(longlong *)(lVar1 + 0xb0) + 8),local_res18,local_res20);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"UUID_UPPER",local_res18[0]);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"UUID_LOWER",local_res20[0]);
(**(code **)(*param_1 + 0x20))
(param_1,iVar4,"CARD_ID",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x18));
(**(code **)(*param_1 + 0x20))
(param_1,iVar4,"FIFA_ID",*(uint *)(*(longlong *)(lVar1 + 0xb0) + 0x18) & 0xffffff);
(**(code **)(*param_1 + 0x20))
(param_1,iVar4,"CARD_TYPE",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x4c));
(**(code **)(*param_1 + 0x20))
(param_1,iVar4,"CARD_OFFERSTATE",*(undefined4 *)(*(longlong *)(lVar1 + 0xb0) + 0x5c)
);
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"IS_WATCHED",*(undefined1 *)(lVar1 + 0xbc));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"INBOX",*(undefined1 *)(lVar1 + 0xbe));
(**(code **)(*param_1 + 0x20))(param_1,iVar4,"IS_GLOW",*(undefined1 *)(lVar1 + 0xbd));
(**(code **)(*param_1 + 0x18))(param_1,iVar4,"TRADE_DATA_AVAILABLE",1);
if (1 < (longlong)local_a0 - (longlong)local_b0) {
if (local_b0 != (undefined1 *)0x0) {
(**(code **)(*local_98 + 0x18))(local_98,local_b0,(int)local_a0 - (int)local_b0);
}
}
if ((1 < local_70 - local_80[0]) && (local_80[0] != 0)) {
(**(code **)(*local_68 + 0x18))(local_68,local_80[0],(int)local_70 - (int)local_80[0]);
}
lVar3 = lVar3 + 0x18;
iVar4 = iVar4 + 1;
} while (lVar3 != *(longlong *)(param_2 + 0x38));
}
return;
}
==============================================================================
== strings: sold / clear / inbox / glow / collect
==============================================================================
sold 0x180228bed 'sold'
sold 0x18022f524 'sold'
Sold 0x1801efaf1 'SoldFromTradePile'
Sold 0x1802302cf 'SoldBid'
Sold 0x1802302df 'SoldBuyNow'
SOLD 0x18020a0c7 'SOLD'
clearSold ABSENT
ClearSold ABSENT
INBOX 0x180215044 'INBOX'
inbox ABSENT
IS_GLOW 0x1802151a0 'IS_GLOW'
glow ABSENT
Glow ABSENT
collect 0x1802307e0 'collector'
Collect 0x1801ec3c1 'CollectedCards'
Collect 0x1801ec3f8 'Collection'
Collect 0x1801ec422 'Collection'
Collect 0x1801ec449 'Collection'
unclaimed 0x18022fd68 'unclaimedPrizesChampionEvents'
claim 0x1802099d1 'claimedPack'
claim 0x1802099e9 'claimedPack2'
claim 0x18022fd6a 'claimedPrizesChampionEvents'