Drift
Python's escrow amount normalizer coerces every validated amount through a Python float() and rejects the call if that coercion overflows to inf. TypeScript's equivalent never coerces string or bigint input to a JS number at all — it validates decimal format/precision only and forwards the value as a string (or stringified bigint), so it has no magnitude ceiling for those input types.
TypeScript SDK
sdks/typescript/pmxt/escrow.ts:84-104 (normalizeUsdcAmount):
function normalizeUsdcAmount(value: number | string | bigint, field: string = "amount"): number | string {
if (typeof value === "bigint") {
if (value <= 0n) { throw new ValidationError(...); }
return value.toString();
}
if (typeof value === "number") {
if (!Number.isFinite(value)) { throw new ValidationError(...); }
validateUsdcDecimal(String(value), field);
return value;
}
if (typeof value === "string") {
const trimmed = value.trim();
validateUsdcDecimal(trimmed, field); // only checks decimal format + ≤6 fractional digits
return trimmed; // kept as string, never coerced to a float — no magnitude check
}
...
}
Python SDK
sdks/python/pmxt/escrow.py:60-83 (_usdc_amount):
amount = Decimal(str(value))
...
scaled = amount * _USDC_SCALE
if scaled != scaled.to_integral_value():
raise ValidationError(f"{field} precision exceeds 6 decimals...", field=field)
json_amount = float(amount)
if not math.isfinite(json_amount):
raise ValidationError(f"{field} is too large to encode as JSON", field=field)
return json_amount
Called from both deposit_tx (escrow.py:129) and withdraw_tx's request branch (escrow.py:156).
Expected
Both languages should apply the same magnitude ceiling (or neither should). Since TypeScript's deposit_tx/withdraw_tx accept number | string | bigint and only the number path is bounded by Number.isFinite, either TypeScript should reject a string/bigint amount whose magnitude would overflow the wire representation Python uses, or Python should stop coercing through float() for the JSON body (matching TypeScript's string-passthrough for large values) so the same input is accepted or rejected consistently in both SDKs.
Impact
A 6-decimal-valid but astronomically large amount (e.g. a caller accidentally passing a value with far too many integer digits, such as through a unit-conversion bug) is silently accepted and forwarded to the server by TypeScript's depositTx/withdrawTx, while Python's deposit_tx/withdraw_tx reject the identical value outright with ValidationError. This is a narrow edge case in practice (requires a value beyond ~1.8e308) but is a real, verified behavioral divergence distinct from the already-tracked precision-loss issue (#1436, which covers float() silently losing precision on in-range values, not the overflow rejection path).
Found by automated SDK cross-language drift audit
Drift
Python's escrow amount normalizer coerces every validated amount through a Python
float()and rejects the call if that coercion overflows toinf. TypeScript's equivalent never coerces string or bigint input to a JSnumberat all — it validates decimal format/precision only and forwards the value as a string (or stringified bigint), so it has no magnitude ceiling for those input types.TypeScript SDK
sdks/typescript/pmxt/escrow.ts:84-104(normalizeUsdcAmount):Python SDK
sdks/python/pmxt/escrow.py:60-83(_usdc_amount):Called from both
deposit_tx(escrow.py:129) andwithdraw_tx'srequestbranch (escrow.py:156).Expected
Both languages should apply the same magnitude ceiling (or neither should). Since TypeScript's
deposit_tx/withdraw_txacceptnumber | string | bigintand only thenumberpath is bounded byNumber.isFinite, either TypeScript should reject a string/bigint amount whose magnitude would overflow the wire representation Python uses, or Python should stop coercing throughfloat()for the JSON body (matching TypeScript's string-passthrough for large values) so the same input is accepted or rejected consistently in both SDKs.Impact
A 6-decimal-valid but astronomically large amount (e.g. a caller accidentally passing a value with far too many integer digits, such as through a unit-conversion bug) is silently accepted and forwarded to the server by TypeScript's
depositTx/withdrawTx, while Python'sdeposit_tx/withdraw_txreject the identical value outright withValidationError. This is a narrow edge case in practice (requires a value beyond ~1.8e308) but is a real, verified behavioral divergence distinct from the already-tracked precision-loss issue (#1436, which coversfloat()silently losing precision on in-range values, not the overflow rejection path).Found by automated SDK cross-language drift audit