T09 · Insecure Skill Coding Practices
- Location
arb_scanner.py:103- Finding
Sequential FOK Orders Do Not Provide Atomic Arbitrage Execution
- Content
View full analysis
Vulnerability Details
File Location:
arb_scanner.py:103-126,arb_scanner.py:211-220; contradictory guarantees inSKILL.md:13andSKILL.md:55-57
Vulnerability Type: Non-atomic financial transaction and inaccurate failure handling
Risk Level: HighVulnerable Code
python def execute_arb(client, arb, deploy_usd): """ Execute both legs as Fill-or-Kill. Both must fill or neither does — no leg risk. """ from py_clob_client.clob_types import MarketOrderArgs, OrderType yes_amount = deploy_usd * arb["yes_price"] / (arb["yes_price"] + arb["no_price"]) no_amount = deploy_usd * arb["no_price"] / (arb["yes_price"] + arb["no_price"]) results = [] for token_id, amount, label in [ (arb["yes_token"], yes_amount, "YES"), (arb["no_token"], no_amount, "NO"), ]: try: args = MarketOrderArgs(token_id=token_id, amount=amount, side="BUY") order = client.create_market_order(args) resp = client.post_order(order, OrderType.FOK) status = resp.get("status", "?") filled = resp.get("success", False) or status == "matched" results.append({"label": label, "filled": filled, "status": status, "resp": resp}) except Exception as e: results.append({"label": label, "filled": False, "status": "error", "error": str(e)}) all_filled = all(r["filled"] for r in results) return all_filled, resultsThe resulting failure is reported as safe:
python ok, results = execute_arb(client, arb, deploy) for r in results: status = "✅" if r["filled"] else "❌" print(f" {status} {r['label']} — {r['status']}") if ok: print(f" 🎉 Both legs filled! Locked in ${profit:.2f} profit at resolution") executed += 1 if rm: rm.record_order() rm.record_order() # two orders (YES + NO) rm.re ...[truncated 2467 chars]- Remediation
View remediation
Remediation Suggestions
- Remove all claims that independent FOK orders are simultaneous or atomic unless the exchange provides an atomic batch-order facility with guaranteed all-or-none behavior.
- Use an exchange-supported atomic or batch transaction mechanism if one is available and verify its actual rollback semantics.
- Immediately re-read executable order-book prices and available quantities before submitting either leg. Do not rely solely on
outcomePricesfrom the market metadata API. - After each submission, distinguish among filled, partially filled, rejected, and indeterminate states using authoritative exchange responses.
- If the first leg fills and the hedge fails, initiate a bounded emergency unwind or hedge procedure. Apply explicit maximum-loss and slippage limits.
- Report partial execution prominently, including the token, amount, execution price, and required operator action. Never print “no position taken” unless account state confirms that assertion.
- Record each successful order and position independently in the risk manager, even when the paired trade fails.
- Reserve sufficient capital for emergency unwinds and stop further opportunities after any partial execution until the account is reconciled.
- Add integration tests covering first-leg success followed by second-leg rejection, timeout, exception, and stale or ambiguous exchange responses.
