Skip to content

SDK drift: Python's user_trade_to_v0 silently drops order_id on the round trip; TypeScript's userTradeToV0 preserves it #1537

Description

@realfishsam

Drift

order_id is parsed on the way in by both languages' user_trade_from_v0/userTradeFromV0, but Python's reverse mapper (user_trade_to_v0) never emits it back out, while TypeScript's userTradeToV0 does.

TypeScript SDK

sdks/typescript/pmxt/hosted-mappers.ts:133-145:

export function userTradeToV0(trade: UserTrade): Record<string, unknown> {
    const out: Record<string, unknown> = {
        id: trade.id,
        side: trade.side,
        amount: Math.round(trade.amount * 1_000_000),
        price: trade.price,
        timestamp: msToTimestamp(trade.timestamp),
    };
    if (trade.orderId !== undefined) out["order_id"] = trade.orderId;
    if (trade.outcomeId !== undefined) out["outcome_id"] = trade.outcomeId;
    if (trade.marketId !== undefined) out["market_id"] = trade.marketId;
    return out;
}

Python SDK

sdks/python/pmxt/_hosted_mappers.py:112-136:

def user_trade_to_v0(trade: UserTrade | Mapping[str, Any]) -> dict[str, Any]:
    data = _as_dict(trade)
    ...
    out = {
        "id": _str_or_none(data.get("id")),
        "market_id": _str_or_none(data.get("market_id")),
        "outcome_id": _str_or_none(data.get("outcome_id")),
        "side": data.get("side"),
        "amount": round(decimal_amount * 1_000_000) if decimal_amount is not None else None,
        "price": _float_or_none(data.get("price")),
        "fee": decimal_fee,
        "timestamp": _ms_to_timestamp(data.get("timestamp")),
    }
    _copy_if_present(out, data, "tx_hash")
    _copy_if_present(out, data, "chain")
    _copy_if_present(out, data, "venue")
    _copy_if_present(out, data, "raw")
    return out

order_id never appears anywhere in this function's output, even though user_trade_from_v0 (line 100) explicitly parses it from the wire.

Expected

Python's user_trade_to_v0 should include order_id in its output when present, matching TypeScript and matching its own user_trade_from_v0.

Impact

Any Python caller that round-trips a UserTrade back to UserTradeV0 JSON (building a request body, caching, replaying a trade) silently loses the order_id field, even though it was populated on the way in. The equivalent TypeScript round trip preserves it — a one-directional, silent data-loss bug specific to the Python reverse mapper.


Found by automated SDK cross-language drift audit

Metadata

Metadata

Assignees

No one assigned

    Labels

    sdk-driftCross-language SDK consistency findings (TypeScript vs Python)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions