test(market): make the sold A/B one-field attributable, add classified differential
The brief's gate: if the harness varies bidState AND coinsProcessed together, the client's reaction is attributable to neither. The env knobs were already orthogonal (--variant and --coins-processed are independent, cp defaults to 0), but sold-wire-check.py was flipping BOTH for variant B as a convenience, which is exactly the contaminated A/B the brief forbids. Fixed: the primary pair now holds coinsProcessed at 0 and asserts the differing-field set is exactly ['bidState']. New scripts/sold-ab-differential.py is the pre-live gate. It settles ONE synthetic sale, then re-reads every seller-facing surface under each variant by restarting only the host (same Core, same DBs, same sale), and diffs with explicit classification -- MISSING / EXTRA / TYPE_MISMATCH / VALUE_MISMATCH -- rather than a boolean "equal?". Two orthogonal pairs: PRIMARY bidState highest vs buyNow, coinsProcessed held at 0 ORTHOGONAL coinsProcessed 0 vs 1, bidState held at highest Result, 36/36: the ONLY finding on /tradePile is VALUE_MISMATCH auctionInfo[0].bidState A='highest' B='buyNow'; /trade/status differs in exactly the same one path; counts are byte-identical. The orthogonal pair's only finding is auctionInfo[0].coinsProcessed. C_cp0's sha256 equals A_highest's, so the capture is reproducible rather than merely consistent. Counts states the live run has to interpret, measured not guessed: S1 0 active + 1 sold -> count 0, selling 0, sold 1 S2 1 active + 1 sold -> count 1 (active mode) vs 2 (membership mode) That divergence IS the open question for the client; production is unchanged. scripts/sold-client-ports.py switches ONLY the two client Blaze port lines, and is built so restoration cannot depend on memory: it records the production values to a sidecar on the client BEFORE the first edit and restore reads that sidecar, refusing if it is absent. It rewrites only known keys (a missing key is an error, never a silent append), re-reads and verifies afterwards, and REFUSES to edit while a FIFA client is running because the hook reads the file at connect time. Phase 0 evidence under docs/evidence/sold-ab-2026-08-18/ with a sha256 per surface, one file per variant so A can never overwrite B. Live client A/B NOT run: a production FIFA session is currently live on 10.10.0.105 (pid 32188), and live-session mutual exclusion applies. The client config was NOT touched -- the switcher's guard refused, as designed. Production untouched: prod-host pid 3631953, coins 29,843,976, /tradePile 0, counts.sold 0, club 1966; nothing under /home/alex/openfut-promotion/state/ opened.
This commit is contained in:
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "caecc67a615ddd0f209b36c7c8ea6a04e0002552d9a42b8eeca20df84feedf3c",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "buyNow"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "0b6e5cc5ab58fdbdd76586b1cff685a562e669fd37750cbd1397db9ee9e97407",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "fab8d43b7ac0f0e09d6902155f7c5f6d91e57b4b595ec8f6093a5a9dcb02bcdb",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "fab8d43b7ac0f0e09d6902155f7c5f6d91e57b4b595ec8f6093a5a9dcb02bcdb",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "caecc67a615ddd0f209b36c7c8ea6a04e0002552d9a42b8eeca20df84feedf3c",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "1",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "6e228a34b5d7fac6bf70710cbd4ca35b58740e04ae455ffeee04b6ce7faab8e5",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "c1557013bf20285d0512ca0d055a16df8036260559d640da11102a2379ac5986",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "c1557013bf20285d0512ca0d055a16df8036260559d640da11102a2379ac5986",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,509 @@
|
|||||||
|
{
|
||||||
|
"counts_states": {
|
||||||
|
"S1_zero_active_one_sold": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"S2_one_active_one_sold_count_active": {
|
||||||
|
"count": 1,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 1,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"S2_one_active_one_sold_count_membership": {
|
||||||
|
"count": 2,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 1,
|
||||||
|
"sold": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diffs": {
|
||||||
|
"orthogonal_coinsprocessed": [
|
||||||
|
{
|
||||||
|
"a": 0,
|
||||||
|
"b": 1,
|
||||||
|
"kind": "VALUE_MISMATCH",
|
||||||
|
"path": "auctionInfo[0].coinsProcessed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"primary_bidstate": [
|
||||||
|
{
|
||||||
|
"a": "highest",
|
||||||
|
"b": "buyNow",
|
||||||
|
"kind": "VALUE_MISMATCH",
|
||||||
|
"path": "auctionInfo[0].bidState"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"generated": "2026-08-18T02:19:25Z",
|
||||||
|
"settlement": {
|
||||||
|
"buyer": 19850,
|
||||||
|
"fee": 7,
|
||||||
|
"instances": 1,
|
||||||
|
"owner": "club-buyer-b",
|
||||||
|
"seller": 1143
|
||||||
|
},
|
||||||
|
"variants": {
|
||||||
|
"A_highest": {
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "caecc67a615ddd0f209b36c7c8ea6a04e0002552d9a42b8eeca20df84feedf3c",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"B_buyNow": {
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "buyNow"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "0b6e5cc5ab58fdbdd76586b1cff685a562e669fd37750cbd1397db9ee9e97407",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "fab8d43b7ac0f0e09d6902155f7c5f6d91e57b4b595ec8f6093a5a9dcb02bcdb",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "buyNow",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "fab8d43b7ac0f0e09d6902155f7c5f6d91e57b4b595ec8f6093a5a9dcb02bcdb",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"C_cp0": {
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "caecc67a615ddd0f209b36c7c8ea6a04e0002552d9a42b8eeca20df84feedf3c",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "0107bf70809cce10fa5fa0f7f4e196f7ffdd6def1b9f63407eeffc7e66f35855",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"D_cp1": {
|
||||||
|
"env": {
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "1",
|
||||||
|
"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest"
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "6e228a34b5d7fac6bf70710cbd4ca35b58740e04ae455ffeee04b6ce7faab8e5",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "c1557013bf20285d0512ca0d055a16df8036260559d640da11102a2379ac5986",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 1,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 212188,
|
||||||
|
"id": 100000178,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rating": 75,
|
||||||
|
"resourceId": 212188,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "c1557013bf20285d0512ca0d055a16df8036260559d640da11102a2379ac5986",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,850 @@
|
|||||||
|
{
|
||||||
|
"captured": "2026-08-18T02:21:49Z",
|
||||||
|
"phase": "0 \u2014 staging validated, sold row present, awaiting live client",
|
||||||
|
"settlement": {
|
||||||
|
"clubs": {
|
||||||
|
"club-buyer-b": 19850,
|
||||||
|
"club-seller-a-cage": 1143
|
||||||
|
},
|
||||||
|
"disposable_owner": [
|
||||||
|
"club-buyer-b"
|
||||||
|
],
|
||||||
|
"fee": 7,
|
||||||
|
"gross": 150,
|
||||||
|
"instances": 1,
|
||||||
|
"proceeds": 143
|
||||||
|
},
|
||||||
|
"sold_row": {
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 232273,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 232273,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 232273,
|
||||||
|
"discardValue": 150,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000012,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 32,
|
||||||
|
"nation": 117,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LB",
|
||||||
|
"rareflag": 1,
|
||||||
|
"rating": 51,
|
||||||
|
"resourceId": 232273,
|
||||||
|
"teamid": 112487,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
},
|
||||||
|
"surfaces": {
|
||||||
|
"club": {
|
||||||
|
"body": {
|
||||||
|
"itemData": [
|
||||||
|
{
|
||||||
|
"assetId": 20801,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 97
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 97
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 86
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 96
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 85
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 20801,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 83906881,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000001,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 38,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LW",
|
||||||
|
"rareflag": 11,
|
||||||
|
"rating": 99,
|
||||||
|
"resourceId": 83906881,
|
||||||
|
"teamid": 243,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 158023,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 94
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 95
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 66
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 158023,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 84044103,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000002,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 52,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "RW",
|
||||||
|
"rareflag": 11,
|
||||||
|
"rating": 98,
|
||||||
|
"resourceId": 84044103,
|
||||||
|
"teamid": 241,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 167495,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 94
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 95
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 99
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 94
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 96
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 167495,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 84053575,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000003,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 19,
|
||||||
|
"nation": 21,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "GK",
|
||||||
|
"rareflag": 11,
|
||||||
|
"rating": 97,
|
||||||
|
"resourceId": 84053575,
|
||||||
|
"teamid": 21,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 176580,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 85
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 93
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 82
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 176580,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 117617092,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000004,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 60,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "ST",
|
||||||
|
"rareflag": 22,
|
||||||
|
"rating": 95,
|
||||||
|
"resourceId": 117617092,
|
||||||
|
"teamid": 241,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 177003,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 78
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 79
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 93
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 76
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 70
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 177003,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 151171947,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000005,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 10,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "CM",
|
||||||
|
"rareflag": 24,
|
||||||
|
"rating": 93,
|
||||||
|
"resourceId": 151171947,
|
||||||
|
"teamid": 243,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 164240,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 77
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 76
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 76
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 93
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 82
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 164240,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 134381968,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000006,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 16,
|
||||||
|
"nation": 54,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "CB",
|
||||||
|
"rareflag": 23,
|
||||||
|
"rating": 92,
|
||||||
|
"resourceId": 134381968,
|
||||||
|
"teamid": 73,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 182493,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 67
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 92
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 83
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 182493,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 151177437,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000007,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 60,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "CB",
|
||||||
|
"rareflag": 24,
|
||||||
|
"rating": 92,
|
||||||
|
"resourceId": 151177437,
|
||||||
|
"teamid": 240,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 168651,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 71
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 87
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 64
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 71
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 168651,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 84054731,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000008,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 10,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "CM",
|
||||||
|
"rareflag": 11,
|
||||||
|
"rating": 92,
|
||||||
|
"resourceId": 84054731,
|
||||||
|
"teamid": 241,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 182521,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 83
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 91
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 82
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 72
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 73
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 182521,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 134400249,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000009,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 53,
|
||||||
|
"nation": 21,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "CM",
|
||||||
|
"rareflag": 23,
|
||||||
|
"rating": 91,
|
||||||
|
"resourceId": 134400249,
|
||||||
|
"teamid": 243,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 197445,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 90
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 77
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 85
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 87
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 87
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 77
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 197445,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 151192389,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000010,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 19,
|
||||||
|
"nation": 4,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LB",
|
||||||
|
"rareflag": 24,
|
||||||
|
"rating": 91,
|
||||||
|
"resourceId": 151192389,
|
||||||
|
"teamid": 21,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assetId": 121939,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 58
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 84
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 86
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 66
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 121939,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 100785235,
|
||||||
|
"discardValue": 1500,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000011,
|
||||||
|
"itemState": "free",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 19,
|
||||||
|
"nation": 21,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "RB",
|
||||||
|
"rareflag": 21,
|
||||||
|
"rating": 90,
|
||||||
|
"resourceId": 100785235,
|
||||||
|
"teamid": 21,
|
||||||
|
"untradeable": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sha256": "0a916e8e129a9c05294784738c305d651beedd58ebd914a6abfa94bd0a59abc7",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"counts": {
|
||||||
|
"body": {
|
||||||
|
"count": 0,
|
||||||
|
"maxAuctionsAllowed": 100,
|
||||||
|
"offered": 0,
|
||||||
|
"selling": 0,
|
||||||
|
"sold": 1
|
||||||
|
},
|
||||||
|
"sha256": "766b1fcc4ca47442e6f570c81583840b6ae8df9cbeae52473b3c100ca881e206",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"tradePile": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 232273,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 232273,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 232273,
|
||||||
|
"discardValue": 150,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000012,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 32,
|
||||||
|
"nation": 117,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LB",
|
||||||
|
"rareflag": 1,
|
||||||
|
"rating": 51,
|
||||||
|
"resourceId": 232273,
|
||||||
|
"teamid": 112487,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143,
|
||||||
|
"duplicateItemIdList": [],
|
||||||
|
"total": 1
|
||||||
|
},
|
||||||
|
"sha256": "c792ebcf179a5c617ffe02859ad39eba830a4d5b5224a0953ecef774ee4cf75b",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_filtered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 232273,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 232273,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 232273,
|
||||||
|
"discardValue": 150,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000012,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 32,
|
||||||
|
"nation": 117,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LB",
|
||||||
|
"rareflag": 1,
|
||||||
|
"rating": 51,
|
||||||
|
"resourceId": 232273,
|
||||||
|
"teamid": 112487,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "db79d9fcfc47ae680b44311598e82918b8afd3284160143c33e525adf693f30e",
|
||||||
|
"status": 200
|
||||||
|
},
|
||||||
|
"trade_status_unfiltered": {
|
||||||
|
"body": {
|
||||||
|
"auctionInfo": [
|
||||||
|
{
|
||||||
|
"bidState": "highest",
|
||||||
|
"buyNowPrice": 150,
|
||||||
|
"coinsProcessed": 0,
|
||||||
|
"currentBid": 150,
|
||||||
|
"expires": 0,
|
||||||
|
"itemData": {
|
||||||
|
"assetId": 232273,
|
||||||
|
"attributeList": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"value": 62
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"value": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"value": 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"value": 49
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"value": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 5,
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cardassetid": 232273,
|
||||||
|
"cardsubtypeid": 0,
|
||||||
|
"contract": 7,
|
||||||
|
"definitionId": 232273,
|
||||||
|
"discardValue": 150,
|
||||||
|
"fitness": 99,
|
||||||
|
"id": 100000012,
|
||||||
|
"itemState": "forSale",
|
||||||
|
"itemType": "player",
|
||||||
|
"leagueId": 32,
|
||||||
|
"nation": 117,
|
||||||
|
"owners": 1,
|
||||||
|
"playStyle": 250,
|
||||||
|
"preferredPosition": "LB",
|
||||||
|
"rareflag": 1,
|
||||||
|
"rating": 51,
|
||||||
|
"resourceId": 232273,
|
||||||
|
"teamid": 112487,
|
||||||
|
"untradeable": false
|
||||||
|
},
|
||||||
|
"sellerEstablished": 1,
|
||||||
|
"sellerName": "CAGE",
|
||||||
|
"startingBid": 150,
|
||||||
|
"tradeId": 900500150,
|
||||||
|
"tradeState": "closed",
|
||||||
|
"watched": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"credits": 1143
|
||||||
|
},
|
||||||
|
"sha256": "db79d9fcfc47ae680b44311598e82918b8afd3284160143c33e525adf693f30e",
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"topology": {
|
||||||
|
"advertises_utas": "http://<host>:8299/",
|
||||||
|
"blaze": [
|
||||||
|
42327,
|
||||||
|
42330,
|
||||||
|
42331
|
||||||
|
],
|
||||||
|
"core": 18081,
|
||||||
|
"utas_host": 8299
|
||||||
|
},
|
||||||
|
"variant": "A_highest (bidState=highest, coinsProcessed=0, countMode=active)"
|
||||||
|
}
|
||||||
Executable
+375
@@ -0,0 +1,375 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PRE-LIVE machine differential for the seller-facing sold-row A/B.
|
||||||
|
|
||||||
|
Purpose: prove, before any operator time is spent at a real FIFA client, that the
|
||||||
|
two variants differ in EXACTLY the field under test. A contaminated A/B cannot
|
||||||
|
attribute the client's reaction to anything, so this gate runs first.
|
||||||
|
|
||||||
|
It performs ONE synthetic settlement, then re-reads every seller-facing surface
|
||||||
|
under each variant by restarting only the host (same Core, same DBs, same sale), and
|
||||||
|
diffs the payloads with explicit classification:
|
||||||
|
|
||||||
|
MISSING key present in A, absent in B
|
||||||
|
EXTRA key absent in A, present in B
|
||||||
|
TYPE_MISMATCH same key, different JSON type
|
||||||
|
VALUE_MISMATCH same key and type, different value
|
||||||
|
|
||||||
|
Two orthogonal pairs are checked:
|
||||||
|
|
||||||
|
PRIMARY bidState highest vs buyNow coinsProcessed held at 0
|
||||||
|
ORTHOGONAL coinsProcessed 0 vs 1 bidState held at highest
|
||||||
|
|
||||||
|
Plus the two counts states the live experiment needs to interpret:
|
||||||
|
|
||||||
|
S1 0 active + 1 sold
|
||||||
|
S2 1 active + 1 sold
|
||||||
|
|
||||||
|
Evidence is written per variant under docs/evidence/sold-ab-<date>/ with a sha256
|
||||||
|
of each payload, so variant A evidence can never be confused with variant B.
|
||||||
|
|
||||||
|
ISOLATION: reuses the verified staging bring-up from sold-wire-check.py, which binds
|
||||||
|
only ephemeral loopback ports, refuses every production port, and opens nothing under
|
||||||
|
/home/alex/openfut-promotion/state/.
|
||||||
|
|
||||||
|
python3 scripts/sold-ab-differential.py [--out DIR] [--keep]
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import importlib.util
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
|
||||||
|
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
# Reuse the proven harness helpers rather than re-deriving them. The filename has
|
||||||
|
# hyphens, so it cannot be imported normally.
|
||||||
|
_spec = importlib.util.spec_from_file_location(
|
||||||
|
"sold_wire_check", os.path.join(REPO, "scripts", "sold-wire-check.py"))
|
||||||
|
H = importlib.util.module_from_spec(_spec)
|
||||||
|
_spec.loader.exec_module(H)
|
||||||
|
|
||||||
|
SURFACES = {
|
||||||
|
"tradePile": "/ut/game/fifa17/tradePile",
|
||||||
|
"counts": "/ut/game/fifa17/tradePile/counts",
|
||||||
|
"trade_status_filtered": f"/ut/game/fifa17/trade/status?tradeIds={H.TRADE_ID}",
|
||||||
|
"trade_status_unfiltered": "/ut/game/fifa17/trade/status",
|
||||||
|
}
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
|
||||||
|
def check(label, ok, detail=""):
|
||||||
|
results.append((label, bool(ok), detail))
|
||||||
|
print(f" [{'PASS' if ok else 'FAIL'}] {label}{(': ' + detail) if detail else ''}")
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
def banner(t):
|
||||||
|
print("\n" + "=" * 74)
|
||||||
|
print(f"== {t}")
|
||||||
|
print("=" * 74)
|
||||||
|
|
||||||
|
|
||||||
|
def jtype(v):
|
||||||
|
if isinstance(v, bool):
|
||||||
|
return "bool"
|
||||||
|
if isinstance(v, int):
|
||||||
|
return "int"
|
||||||
|
if isinstance(v, float):
|
||||||
|
return "float"
|
||||||
|
if isinstance(v, str):
|
||||||
|
return "str"
|
||||||
|
if isinstance(v, list):
|
||||||
|
return "list"
|
||||||
|
if isinstance(v, dict):
|
||||||
|
return "dict"
|
||||||
|
return "null"
|
||||||
|
|
||||||
|
|
||||||
|
def classify(a, b, path=""):
|
||||||
|
"""Recursive classified diff. Returns a list of (kind, path, a, b)."""
|
||||||
|
out = []
|
||||||
|
if jtype(a) != jtype(b):
|
||||||
|
return [("TYPE_MISMATCH", path or "<root>", jtype(a), jtype(b))]
|
||||||
|
if isinstance(a, dict):
|
||||||
|
for k in a:
|
||||||
|
if k not in b:
|
||||||
|
out.append(("MISSING", f"{path}.{k}".lstrip("."), a[k], None))
|
||||||
|
for k in b:
|
||||||
|
if k not in a:
|
||||||
|
out.append(("EXTRA", f"{path}.{k}".lstrip("."), None, b[k]))
|
||||||
|
for k in a:
|
||||||
|
if k in b:
|
||||||
|
out += classify(a[k], b[k], f"{path}.{k}".lstrip("."))
|
||||||
|
return out
|
||||||
|
if isinstance(a, list):
|
||||||
|
if len(a) != len(b):
|
||||||
|
out.append(("VALUE_MISMATCH", f"{path}[len]", len(a), len(b)))
|
||||||
|
for i, (x, y) in enumerate(zip(a, b)):
|
||||||
|
out += classify(x, y, f"{path}[{i}]")
|
||||||
|
return out
|
||||||
|
if a != b:
|
||||||
|
out.append(("VALUE_MISMATCH", path or "<root>", a, b))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def capture(port):
|
||||||
|
"""Every seller-facing surface, as parsed JSON plus a sha256 of the raw bytes."""
|
||||||
|
snap = {}
|
||||||
|
for name, path in SURFACES.items():
|
||||||
|
st, body = H.req(port, "GET", path)
|
||||||
|
raw = json.dumps(body, sort_keys=True, separators=(",", ":")).encode()
|
||||||
|
snap[name] = {
|
||||||
|
"status": st,
|
||||||
|
"body": body,
|
||||||
|
"sha256": hashlib.sha256(raw).hexdigest(),
|
||||||
|
}
|
||||||
|
return snap
|
||||||
|
|
||||||
|
|
||||||
|
def sold_row(snap):
|
||||||
|
rows = snap["tradePile"]["body"].get("auctionInfo", [])
|
||||||
|
return rows[0] if rows else {}
|
||||||
|
|
||||||
|
|
||||||
|
def report_diff(label, a, b, allowed):
|
||||||
|
"""Print the classified diff and assert only `allowed` paths differ."""
|
||||||
|
diffs = classify(a, b)
|
||||||
|
print(f" classified diff ({len(diffs)} finding(s)):")
|
||||||
|
for kind, path, av, bv in diffs:
|
||||||
|
print(f" {kind:15s} {path:34s} A={av!r} B={bv!r}")
|
||||||
|
kinds = {k for k, _, _, _ in diffs}
|
||||||
|
for bad in ("MISSING", "EXTRA", "TYPE_MISMATCH"):
|
||||||
|
check(f"{label}: no {bad}", bad not in kinds,
|
||||||
|
", ".join(p for k, p, _, _ in diffs if k == bad) or "none")
|
||||||
|
paths = sorted(p for _, p, _, _ in diffs)
|
||||||
|
check(f"{label}: only {allowed} differ", paths == sorted(allowed), str(paths))
|
||||||
|
return diffs
|
||||||
|
|
||||||
|
|
||||||
|
def restart_host(state, variant, coins_processed):
|
||||||
|
"""Same Core, same DBs, same settled sale — only the host's variant changes."""
|
||||||
|
if state.get("host"):
|
||||||
|
state["host"].terminate()
|
||||||
|
state["host"].wait(timeout=20)
|
||||||
|
state["host"] = H.start_host(state["tmp"], state["host_port"], state["core_port"],
|
||||||
|
state["host_log"], variant,
|
||||||
|
coins_processed=coins_processed)
|
||||||
|
return state["host"]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--out", default=os.path.join(
|
||||||
|
REPO, "docs/evidence", f"sold-ab-{time.strftime('%Y-%m-%d')}"))
|
||||||
|
ap.add_argument("--keep", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
os.makedirs(args.out, exist_ok=True)
|
||||||
|
|
||||||
|
tmp = tempfile.mkdtemp(prefix="openfut-sold-ab-")
|
||||||
|
state = {"tmp": tmp, "host": None}
|
||||||
|
evidence = {"generated": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||||
|
"variants": {}, "diffs": {}, "counts_states": {}}
|
||||||
|
try:
|
||||||
|
state["core_port"] = H.free_port()
|
||||||
|
state["host_port"] = H.free_port()
|
||||||
|
state["core_log"] = os.path.join(tmp, "core.log")
|
||||||
|
state["host_log"] = os.path.join(tmp, "host.log")
|
||||||
|
banner("ISOLATED STAGING")
|
||||||
|
print(f" tmp {tmp}\n core 127.0.0.1:{state['core_port']}"
|
||||||
|
f"\n host 127.0.0.1:{state['host_port']}")
|
||||||
|
core, core_db = H.start_core(tmp, state["core_port"], state["core_log"])
|
||||||
|
state["core"] = core
|
||||||
|
state["core_db"] = core_db
|
||||||
|
mdb = os.path.join(tmp, "market.db")
|
||||||
|
|
||||||
|
# ---- one authentic listing + one real settlement -------------------
|
||||||
|
restart_host(state, "highest", "0")
|
||||||
|
con = sqlite3.connect(mdb)
|
||||||
|
con.execute(
|
||||||
|
"INSERT INTO listings (listing_id, card_id, core_item_id, wire_item_id, "
|
||||||
|
"wire_resource_id, start_price, buy_now_price, owner, state, created_at, "
|
||||||
|
"item_json, duration_secs) VALUES (?,?,?,?,?,?,?,?, 'active', ?, ?, ?)",
|
||||||
|
(H.TRADE_ID, H.CARD, H.ITEM, 100000178, 212188, H.GROSS, H.GROSS, "CAGE",
|
||||||
|
str(int(time.time() * 1000)), json.dumps({
|
||||||
|
"id": 100000178, "resourceId": 212188, "rating": 75,
|
||||||
|
"preferredPosition": "ST", "itemState": "forSale",
|
||||||
|
"untradeable": False, "assetId": 212188}), 3600))
|
||||||
|
con.commit(); con.close()
|
||||||
|
banner(f"SETTLE — synthetic Buyer B buys at {H.GROSS}")
|
||||||
|
out = subprocess.run(
|
||||||
|
[os.path.join(REPO, "target/release/staging_sell"),
|
||||||
|
"--market-db", mdb, "--core-url", f"http://127.0.0.1:{state['core_port']}",
|
||||||
|
"--trade-id", H.TRADE_ID, "--item", H.ITEM,
|
||||||
|
"--seller", H.SELLER_CLUB, "--buyer", H.BUYER_CLUB,
|
||||||
|
"--gross", str(H.GROSS)],
|
||||||
|
capture_output=True, text=True, timeout=120)
|
||||||
|
print(" " + "\n ".join((out.stdout + out.stderr).strip().splitlines()))
|
||||||
|
check("settlement succeeded", out.returncode == 0)
|
||||||
|
own, n, coins = H.owner_of(core_db, H.ITEM)
|
||||||
|
fee = H.GROSS * 5 // 100
|
||||||
|
check("ownership -> buyer", own == H.BUYER_CLUB, str(own))
|
||||||
|
check("exactly one authoritative instance", n == 1, str(n))
|
||||||
|
check("seller credited net", coins[H.SELLER_CLUB] == 1000 + H.GROSS - fee,
|
||||||
|
str(coins[H.SELLER_CLUB]))
|
||||||
|
check("buyer debited gross", coins[H.BUYER_CLUB] == 20000 - H.GROSS,
|
||||||
|
str(coins[H.BUYER_CLUB]))
|
||||||
|
economy = {"seller": coins[H.SELLER_CLUB], "buyer": coins[H.BUYER_CLUB],
|
||||||
|
"fee": fee, "owner": own, "instances": n}
|
||||||
|
evidence["settlement"] = economy
|
||||||
|
|
||||||
|
# ---- PRIMARY A/B: bidState only ------------------------------------
|
||||||
|
banner("PRIMARY A/B — bidState highest vs buyNow (coinsProcessed held at 0)")
|
||||||
|
snaps = {}
|
||||||
|
for name, variant in (("A_highest", "highest"), ("B_buyNow", "buyNow")):
|
||||||
|
restart_host(state, variant, "0")
|
||||||
|
snaps[name] = capture(state["host_port"])
|
||||||
|
evidence["variants"][name] = {
|
||||||
|
"env": {"OPENFUT_FIFA17_SOLD_EXPERIMENT": variant,
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": "0"},
|
||||||
|
"surfaces": snaps[name],
|
||||||
|
}
|
||||||
|
row = sold_row(snaps[name])
|
||||||
|
print(f" {name}: tradeState={row.get('tradeState')} "
|
||||||
|
f"bidState={row.get('bidState')} currentBid={row.get('currentBid')} "
|
||||||
|
f"coinsProcessed={row.get('coinsProcessed')} atoms={len(row)}")
|
||||||
|
# Every variant must independently be a well-formed sold row.
|
||||||
|
check(f"{name}: tradeState closed", row.get("tradeState") == "closed")
|
||||||
|
check(f"{name}: twelve atoms", len(row) == 12, str(len(row)))
|
||||||
|
check(f"{name}: currentBid == gross", row.get("currentBid") == H.GROSS)
|
||||||
|
check(f"{name}: expires 0", row.get("expires") == 0)
|
||||||
|
check(f"{name}: counts.sold == 1",
|
||||||
|
snaps[name]["counts"]["body"].get("sold") == 1)
|
||||||
|
evidence["diffs"]["primary_bidstate"] = [
|
||||||
|
{"kind": k, "path": p, "a": av, "b": bv}
|
||||||
|
for k, p, av, bv in report_diff(
|
||||||
|
"PRIMARY", snaps["A_highest"]["tradePile"]["body"],
|
||||||
|
snaps["B_buyNow"]["tradePile"]["body"],
|
||||||
|
["auctionInfo[0].bidState"])
|
||||||
|
]
|
||||||
|
# /trade/status must move in lockstep, or the screen would contradict itself.
|
||||||
|
report_diff("PRIMARY /trade/status",
|
||||||
|
snaps["A_highest"]["trade_status_filtered"]["body"],
|
||||||
|
snaps["B_buyNow"]["trade_status_filtered"]["body"],
|
||||||
|
["auctionInfo[0].bidState"])
|
||||||
|
report_diff("PRIMARY counts",
|
||||||
|
snaps["A_highest"]["counts"]["body"],
|
||||||
|
snaps["B_buyNow"]["counts"]["body"], [])
|
||||||
|
|
||||||
|
# ---- ORTHOGONAL A/B: coinsProcessed only ---------------------------
|
||||||
|
banner("ORTHOGONAL A/B — coinsProcessed 0 vs 1 (bidState held at highest)")
|
||||||
|
for name, cp in (("C_cp0", "0"), ("D_cp1", "1")):
|
||||||
|
restart_host(state, "highest", cp)
|
||||||
|
snaps[name] = capture(state["host_port"])
|
||||||
|
evidence["variants"][name] = {
|
||||||
|
"env": {"OPENFUT_FIFA17_SOLD_EXPERIMENT": "highest",
|
||||||
|
"OPENFUT_FIFA17_SOLD_COINS_PROCESSED": cp},
|
||||||
|
"surfaces": snaps[name],
|
||||||
|
}
|
||||||
|
row = sold_row(snaps[name])
|
||||||
|
print(f" {name}: bidState={row.get('bidState')} "
|
||||||
|
f"coinsProcessed={row.get('coinsProcessed')}")
|
||||||
|
evidence["diffs"]["orthogonal_coinsprocessed"] = [
|
||||||
|
{"kind": k, "path": p, "a": av, "b": bv}
|
||||||
|
for k, p, av, bv in report_diff(
|
||||||
|
"ORTHOGONAL", snaps["C_cp0"]["tradePile"]["body"],
|
||||||
|
snaps["D_cp1"]["tradePile"]["body"],
|
||||||
|
["auctionInfo[0].coinsProcessed"])
|
||||||
|
]
|
||||||
|
check("C_cp0 is identical to A_highest (same env => same bytes)",
|
||||||
|
snaps["C_cp0"]["tradePile"]["sha256"]
|
||||||
|
== snaps["A_highest"]["tradePile"]["sha256"],
|
||||||
|
"reproducible")
|
||||||
|
|
||||||
|
# ---- counts states the live run must interpret ----------------------
|
||||||
|
banner("COUNTS STATES")
|
||||||
|
restart_host(state, "highest", "0")
|
||||||
|
s1 = H.req(state["host_port"], "GET", SURFACES["counts"])[1]
|
||||||
|
print(f" S1 0 active + 1 sold -> {json.dumps(s1)}")
|
||||||
|
con = sqlite3.connect(mdb)
|
||||||
|
con.execute(
|
||||||
|
"INSERT INTO listings (listing_id, card_id, core_item_id, wire_item_id, "
|
||||||
|
"wire_resource_id, start_price, buy_now_price, owner, state, created_at, "
|
||||||
|
"item_json, duration_secs) VALUES (?,?,?,?,?,?,?,?, 'active', ?, ?, ?)",
|
||||||
|
("900500999", H.CARD, "core-second-x", 100000179, 212188, 300, 400, "CAGE",
|
||||||
|
str(int(time.time() * 1000)), json.dumps({
|
||||||
|
"id": 100000179, "resourceId": 212188, "rating": 75,
|
||||||
|
"preferredPosition": "ST", "itemState": "forSale",
|
||||||
|
"untradeable": False, "assetId": 212188}), 3600))
|
||||||
|
con.commit(); con.close()
|
||||||
|
s2_active = H.req(state["host_port"], "GET", SURFACES["counts"])[1]
|
||||||
|
print(f" S2 1 active + 1 sold -> {json.dumps(s2_active)} (count mode: active)")
|
||||||
|
restart_host(state, "highest", "0")
|
||||||
|
os.environ["_"] = "_" # no-op; count mode is a host env, set below
|
||||||
|
state["host"].terminate(); state["host"].wait(timeout=20)
|
||||||
|
state["host"] = H.start_host(tmp, state["host_port"], state["core_port"],
|
||||||
|
state["host_log"], "highest",
|
||||||
|
coins_processed="0",
|
||||||
|
count_mode="active_plus_sold")
|
||||||
|
s2_member = H.req(state["host_port"], "GET", SURFACES["counts"])[1]
|
||||||
|
print(f" S2 1 active + 1 sold -> {json.dumps(s2_member)} (count mode: membership)")
|
||||||
|
evidence["counts_states"] = {
|
||||||
|
"S1_zero_active_one_sold": s1,
|
||||||
|
"S2_one_active_one_sold_count_active": s2_active,
|
||||||
|
"S2_one_active_one_sold_count_membership": s2_member,
|
||||||
|
}
|
||||||
|
check("S1 reports sold 1 / selling 0", s1.get("sold") == 1 and s1.get("selling") == 0)
|
||||||
|
check("S2 reports sold 1 / selling 1",
|
||||||
|
s2_active.get("sold") == 1 and s2_active.get("selling") == 1)
|
||||||
|
check("S2 count differs by mode (1 vs 2) — the open question for the client",
|
||||||
|
s2_active.get("count") == 1 and s2_member.get("count") == 2,
|
||||||
|
f"{s2_active.get('count')} vs {s2_member.get('count')}")
|
||||||
|
|
||||||
|
# ---- emit evidence --------------------------------------------------
|
||||||
|
banner("EVIDENCE")
|
||||||
|
for name, v in evidence["variants"].items():
|
||||||
|
path = os.path.join(args.out, f"{name}.json")
|
||||||
|
with open(path, "w") as f:
|
||||||
|
json.dump(v, f, indent=2, sort_keys=True)
|
||||||
|
print(f" {os.path.relpath(path, REPO)} "
|
||||||
|
f"tradePile sha256={v['surfaces']['tradePile']['sha256'][:16]}…")
|
||||||
|
idx = os.path.join(args.out, "differential.json")
|
||||||
|
with open(idx, "w") as f:
|
||||||
|
json.dump(evidence, f, indent=2, sort_keys=True)
|
||||||
|
print(f" {os.path.relpath(idx, REPO)}")
|
||||||
|
|
||||||
|
banner("PRODUCTION UNTOUCHED")
|
||||||
|
alive = subprocess.run(["ps", "-o", "pid=", "-p", "3631953"],
|
||||||
|
capture_output=True, text=True).stdout.strip()
|
||||||
|
check("prod-host pid 3631953 alive", alive == "3631953", alive or "gone")
|
||||||
|
|
||||||
|
banner("RESULT")
|
||||||
|
passed = sum(1 for _, ok, _ in results if ok)
|
||||||
|
failed = [l for l, ok, _ in results if not ok]
|
||||||
|
print(f" {passed}/{len(results)} checks passed")
|
||||||
|
if failed:
|
||||||
|
print(" FAILED: " + "; ".join(failed))
|
||||||
|
print("\n A/B IS CONTAMINATED — do NOT run the live experiment")
|
||||||
|
return 1
|
||||||
|
print("\n A/B IS CLEAN — one-field attribution proven; ready for the live client")
|
||||||
|
return 0
|
||||||
|
finally:
|
||||||
|
for k in ("host", "core"):
|
||||||
|
p = state.get(k)
|
||||||
|
if p:
|
||||||
|
try:
|
||||||
|
p.terminate(); p.wait(timeout=15)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
p.kill()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if args.keep:
|
||||||
|
print(f"\n kept {tmp}")
|
||||||
|
else:
|
||||||
|
shutil.rmtree(tmp, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Executable
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Switch the FIFA client's Blaze ports between PRODUCTION and STAGING, reversibly.
|
||||||
|
|
||||||
|
The staging sold experiment needs the client to talk to the staging Blaze (which
|
||||||
|
advertises the staging UTAS base). The ONLY client change required is the two Blaze
|
||||||
|
port lines in `openfut.cfg`; `host` and `https_port` are left alone.
|
||||||
|
|
||||||
|
Safety properties, in order of importance:
|
||||||
|
|
||||||
|
* The production values are recorded to a sidecar file on the client BEFORE the
|
||||||
|
first edit, and `restore` reads that sidecar rather than assuming what
|
||||||
|
production was. If the sidecar is missing, restore refuses.
|
||||||
|
* `restore` is idempotent and safe to run at any time, including after a crash.
|
||||||
|
* Every operation re-reads the file afterwards and prints it, so the result is
|
||||||
|
verified rather than assumed.
|
||||||
|
* It refuses to edit while a FIFA client is running: the hook reads this file at
|
||||||
|
connect time, so changing it under a live session is unsafe.
|
||||||
|
* Only the two known keys are rewritten. Unknown lines are passed through
|
||||||
|
untouched, and a missing key is an error rather than a silent append.
|
||||||
|
|
||||||
|
python3 scripts/sold-client-ports.py show
|
||||||
|
python3 scripts/sold-client-ports.py staging # 42327 / 42330
|
||||||
|
python3 scripts/sold-client-ports.py restore # back to the recorded values
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
CLIENT = "alex@10.10.0.105"
|
||||||
|
CFG = "/mnt/games/FIFA 17/openfut.cfg"
|
||||||
|
SIDECAR = "/mnt/games/FIFA 17/openfut.cfg.openfut-prod-ports"
|
||||||
|
KEYS = ("blaze_redirector_port", "blaze_main_port")
|
||||||
|
STAGING = {"blaze_redirector_port": "42327", "blaze_main_port": "42330"}
|
||||||
|
|
||||||
|
|
||||||
|
def ssh(script):
|
||||||
|
r = subprocess.run(["ssh", "-o", "BatchMode=yes", CLIENT, script],
|
||||||
|
capture_output=True, text=True, timeout=60)
|
||||||
|
if r.returncode != 0:
|
||||||
|
raise SystemExit(f"ssh failed ({r.returncode}): {r.stderr.strip()}")
|
||||||
|
return r.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def read_cfg():
|
||||||
|
return ssh(f'cat "{CFG}"')
|
||||||
|
|
||||||
|
|
||||||
|
def parse(text):
|
||||||
|
out = {}
|
||||||
|
for line in text.splitlines():
|
||||||
|
if "=" in line and not line.strip().startswith("#"):
|
||||||
|
k, _, v = line.partition("=")
|
||||||
|
out[k.strip()] = v.strip()
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def fifa_running():
|
||||||
|
return bool(ssh("pgrep -f FIFA17.exe || true").strip())
|
||||||
|
|
||||||
|
|
||||||
|
def show():
|
||||||
|
text = read_cfg()
|
||||||
|
print(f"--- {CFG}")
|
||||||
|
print(text.rstrip())
|
||||||
|
cur = parse(text)
|
||||||
|
print("--- blaze ports:", {k: cur.get(k) for k in KEYS})
|
||||||
|
side = ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip()
|
||||||
|
print("--- recorded production ports:", side or "(none recorded yet)")
|
||||||
|
print("--- FIFA client running:", "YES" if fifa_running() else "no")
|
||||||
|
return cur
|
||||||
|
|
||||||
|
|
||||||
|
def write_ports(values, label):
|
||||||
|
if fifa_running():
|
||||||
|
raise SystemExit(
|
||||||
|
"REFUSING to edit openfut.cfg while a FIFA client is running.\n"
|
||||||
|
"The hook reads this file at connect time; exit FIFA first."
|
||||||
|
)
|
||||||
|
text = read_cfg()
|
||||||
|
cur = parse(text)
|
||||||
|
for k in KEYS:
|
||||||
|
if k not in cur:
|
||||||
|
raise SystemExit(f"key {k!r} is absent from {CFG}; refusing to guess it")
|
||||||
|
# Record production values once, before the first mutation, so restore never
|
||||||
|
# has to assume what they were.
|
||||||
|
existing_sidecar = ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip()
|
||||||
|
if not existing_sidecar and label == "staging":
|
||||||
|
rec = "\n".join(f"{k}={cur[k]}" for k in KEYS)
|
||||||
|
ssh(f'cat > "{SIDECAR}" <<\'EOF\'\n{rec}\nEOF')
|
||||||
|
print(f"recorded production ports to {SIDECAR}:\n{rec}")
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for line in text.splitlines():
|
||||||
|
k = line.partition("=")[0].strip()
|
||||||
|
out.append(f"{k}={values[k]}" if k in values else line)
|
||||||
|
body = "\n".join(out) + "\n"
|
||||||
|
ssh(f'cat > "{CFG}" <<\'EOF\'\n{body.rstrip()}\nEOF')
|
||||||
|
after = parse(read_cfg())
|
||||||
|
for k, v in values.items():
|
||||||
|
if after.get(k) != v:
|
||||||
|
raise SystemExit(f"VERIFY FAILED: {k} is {after.get(k)!r}, expected {v!r}")
|
||||||
|
print(f"--- now pointing at {label}")
|
||||||
|
print(read_cfg().rstrip())
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2 or sys.argv[1] not in ("show", "staging", "restore"):
|
||||||
|
print(__doc__)
|
||||||
|
return 2
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
if cmd == "show":
|
||||||
|
show()
|
||||||
|
return 0
|
||||||
|
if cmd == "staging":
|
||||||
|
write_ports(STAGING, "staging")
|
||||||
|
return 0
|
||||||
|
side = ssh(f'cat "{SIDECAR}" 2>/dev/null || true').strip()
|
||||||
|
if not side:
|
||||||
|
raise SystemExit(
|
||||||
|
f"no recorded production ports at {SIDECAR}; refusing to guess.\n"
|
||||||
|
"Set them by hand and verify with `show`."
|
||||||
|
)
|
||||||
|
write_ports(parse(side), "production (restored)")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -299,8 +299,11 @@ def main():
|
|||||||
|
|
||||||
banner("VARIANT B — same state, restart host with closed / buyNow")
|
banner("VARIANT B — same state, restart host with closed / buyNow")
|
||||||
host.terminate(); host.wait(timeout=20); procs.remove(host)
|
host.terminate(); host.wait(timeout=20); procs.remove(host)
|
||||||
|
# coinsProcessed is held CONSTANT at 0 here: the primary A/B must be
|
||||||
|
# attributable to bidState alone. The orthogonal coinsProcessed test is a
|
||||||
|
# separate variant pair (see scripts/sold-ab-differential.py).
|
||||||
host = start_host(tmp, host_port, core_port, host_log, "buyNow",
|
host = start_host(tmp, host_port, core_port, host_log, "buyNow",
|
||||||
coins_processed="1", count_mode="active_plus_sold")
|
coins_processed="0", count_mode="active_plus_sold")
|
||||||
procs.append(host)
|
procs.append(host)
|
||||||
st, pileb = req(host_port, "GET", "/ut/game/fifa17/tradePile")
|
st, pileb = req(host_port, "GET", "/ut/game/fifa17/tradePile")
|
||||||
st2, countsb = req(host_port, "GET", "/ut/game/fifa17/tradePile/counts")
|
st2, countsb = req(host_port, "GET", "/ut/game/fifa17/tradePile/counts")
|
||||||
@@ -308,13 +311,13 @@ def main():
|
|||||||
print(f" bidState={b_row['bidState']} coinsProcessed={b_row['coinsProcessed']} "
|
print(f" bidState={b_row['bidState']} coinsProcessed={b_row['coinsProcessed']} "
|
||||||
f"counts={json.dumps(countsb)}")
|
f"counts={json.dumps(countsb)}")
|
||||||
check("bidState buyNow (variant B)", b_row.get("bidState") == "buyNow")
|
check("bidState buyNow (variant B)", b_row.get("bidState") == "buyNow")
|
||||||
check("coinsProcessed 1 when asked", b_row.get("coinsProcessed") == 1)
|
check("coinsProcessed held constant at 0", b_row.get("coinsProcessed") == 0)
|
||||||
check("count_mode active_plus_sold counts the sold row",
|
check("count_mode active_plus_sold counts the sold row",
|
||||||
countsb.get("count") == 1 and countsb.get("sold") == 1,
|
countsb.get("count") == 1 and countsb.get("sold") == 1,
|
||||||
json.dumps(countsb))
|
json.dumps(countsb))
|
||||||
differing = sorted(k for k in a_row if a_row.get(k) != b_row.get(k))
|
differing = sorted(k for k in a_row if a_row.get(k) != b_row.get(k))
|
||||||
check("A/B differ ONLY in bidState and coinsProcessed",
|
check("A/B differ ONLY in bidState (one-field attribution)",
|
||||||
differing == ["bidState", "coinsProcessed"], str(differing))
|
differing == ["bidState"], str(differing))
|
||||||
|
|
||||||
banner("CLEAR — the PE-proven bulk verb DELETE .../trade/sold")
|
banner("CLEAR — the PE-proven bulk verb DELETE .../trade/sold")
|
||||||
pre_coins = owner_of(core_db, ITEM)[2]
|
pre_coins = owner_of(core_db, ITEM)[2]
|
||||||
|
|||||||
Reference in New Issue
Block a user