Skip to content

fix: auto-refresh balance cache on stale-balance order failures#55

Open
qtzx06 wants to merge 1 commit intoPolymarket:mainfrom
qtzx06:fix/fak-balance-retry
Open

fix: auto-refresh balance cache on stale-balance order failures#55
qtzx06 wants to merge 1 commit intoPolymarket:mainfrom
qtzx06:fix/fak-balance-retry

Conversation

@qtzx06
Copy link

@qtzx06 qtzx06 commented Mar 20, 2026

Problem

FAK and FOK orders fail with 400 "not enough balance / allowance" even when the user has sufficient USDC.e (#54). This happens because the CLOB server caches wallet balances — if the cache is stale (e.g. after a deposit, approval, or recent trade), the server rejects orders based on outdated balance data.

Users currently have to manually run polymarket clob update-balance --asset-type collateral before retrying, but nothing tells them to do that. The error message gives no indication that the balance is cached or that a refresh might help.

Fix

When post_order fails with "not enough balance", the CLI now:

  1. Calls update_balance_allowance to refresh the server's cached balance
  2. Rebuilds the order (new salt for the fresh signature)
  3. Re-signs and retries once

If the retry also fails, the original error propagates — so users with genuinely insufficient funds still get the correct error.

This applies to both create-order (limit orders) and market-order paths, covering all order types (GTC, GTD, FOK, FAK).

Implementation

One file changed (src/commands/clob.rs):

  • Added is_balance_error() helper that checks the SDK error message
  • Wrapped post_order calls in create-order and market-order with retry-on-balance-error logic
  • Converted CLI enums to SDK types once upfront so they can be reused on retry

Test plan

  • cargo test — 131 tests pass (82 unit + 49 integration)
  • cargo clippy -- -D warnings — clean
  • cargo fmt --check — clean
  • Verified wallet connection works with live CLOB (clob balance returns $690)

Note

Medium Risk
Touches authenticated order submission paths and introduces automatic retry behavior, which could mask some error cases or add unexpected extra requests. Scope is limited to a single retry on a specific error string match.

Overview
Improves CLI resiliency for create-order (limit) and market-order by detecting CLOB "not enough balance / allowance" failures, refreshing the server-side collateral balance cache via update_balance_allowance, then rebuilding/re-signing and retrying post_order once.

Refactors these paths to parse token_id, Side, and OrderType once up front so the same inputs can be reused on the retry, and adds an is_balance_error helper to gate the behavior.

Written by Cursor Bugbot for commit bb38955. This will update automatically on new commits. Configure here.

the CLOB server caches wallet balances. when the cache is stale,
orders fail with "not enough balance / allowance" even though the
user has sufficient funds. this is especially common with FAK/FOK
orders that execute immediately.

now create-order and market-order detect this error, call
update_balance_allowance to refresh the server's cache, rebuild
the order with a fresh salt, and retry once before giving up.

closes Polymarket#54
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

let req = BalanceAllowanceRequest::builder()
.asset_type(AssetType::Collateral)
.build();
let _ = client.update_balance_allowance(req).await;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retry always refreshes collateral, ignoring sell-side conditional tokens

Medium Severity

The retry logic always refreshes AssetType::Collateral regardless of order side. For sell orders, a "not enough balance" error indicates stale conditional token (shares) balance, not collateral. Refreshing collateral won't fix that, so the retry will always fail for sell-side stale-balance errors. Additionally, conditional balance updates require a token_id (as seen in the manual UpdateBalance command), which the retry code doesn't provide.

Additional Locations (1)
Fix in Cursor Fix in Web

client.post_order(order).await?
}
Err(e) => return Err(e.into()),
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated retry logic across order command paths

Low Severity

The balance-refresh-and-retry logic is duplicated nearly identically between the CreateOrder and MarketOrder match arms — including the is_balance_error guard, the eprintln message, the BalanceAllowanceRequest construction, and the re-sign-and-post pattern. Fixing bugs in this retry logic (like the asset-type issue) requires updating both copies in lockstep, increasing the risk of inconsistency.

Additional Locations (1)
Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant