Skip to content

Wallet Routing And Funds Recovery

Purpose

Use this guide when Polymarket funds or positions appear on the wrong wallet, a Deposit Wallet or Safe route looks inconsistent, or a deposit did not arrive where you expected. Start with the simple recovery commands, then use the advanced diagnostics only when the CLI or support asks for them.

This guide helps you collect evidence and choose the next safe command. It does not prove that an affected account has been recovered.

Plain-English Wallet Terms

These terms appear throughout this guide.

  • Bullpen-managed 0x wallet — an Ethereum 0x... address controlled by your Bullpen account. An account can have more than one. It can authorize transfers, but it is usually not where your Polymarket funds live.
  • Imported external EVM wallet — an EVM account added for visibility. It cannot be selected as a Bullpen-managed wallet and is unavailable for managed transfer, ownership-proven Polymarket recovery, or managed trading. Inspect its stable capability flags with bullpen wallet list --output json.
  • Selected trading wallet — for each Bullpen-managed 0x wallet, Bullpen chooses which Polymarket wallet is active for trading: Deposit Wallet, Safe, or Proxy. The CLI follows that choice; you do not pick it by hand during normal trading.
  • Assets outside the selected trading wallet — funds or positions sitting on a Polymarket wallet that is not the selected trading wallet. They are not lost, but the active trading flow cannot use them until they are moved to the selected wallet.
  • Deposit Wallet derivation — the proof path used to compute the expected Deposit Wallet address. If output says the CLI used a fallback proof path, treat that as degraded evidence and retry diagnostics when Polygon RPC is healthy before using it for a support decision.
  • Legacy pre-beacon Deposit Wallet — an address derived by Polymarket's older Deposit Wallet factory before the June 2026 beacon migration. Deployed legacy Deposit Wallets can be recovery sources when ownership and asset evidence are proven. Some legacy or routing-only cases still require support-side recovery.

Start Here: Symptom → Command

Match your symptom, run the first command, then follow the linked section below. Preview and support commands are read-only; live recovery commands still require explicit confirmation.

Symptom Run this first Then
"My Polymarket balance shows $0 but I deposited" bullpen recover funds --dry-run --output json Review the plan. Submit only when it says the CLI can move funds from an owned wallet.
"My deposit never arrived" bullpen funds diagnose-deposit --tx <TX_HASH> --chain <CHAIN> --token <TOKEN> --address <RECIPIENT_ADDRESS> --output json Wrong chain / wrong token / exchange address -> include the JSON in your support ticket.
"Funds are on the wrong wallet" bullpen recover funds --dry-run --output json Stranded assets reported → Preview Recovery Workflows.
"I moved funds to my Safe and can't trade them" bullpen recover funds --dry-run --output json Execute only when the preview clearly says the CLI can move owned funds and the source, destination, route, and assets match your intent.
"I can't trade / trading-wallet rejection" bullpen fix --refresh Follow the single next action it prints. Do not switch wallet kind or retry the trade unless fix or Bullpen Support gives that exact step.
"I can't redeem / STATE_FAILED" bullpen recover redeem --dry-run --output json Capture the dry-run JSON; see Trading → Redeem.
"Wallet routing disagreement" bullpen recover polymarket --refresh --output json Do not force a wallet kind, relogin repeatedly, or retry money movement. Follow recovery_plan.next_action; create a support bundle only when the plan says support is needed.
"The CLI reports legacy Deposit Wallet recovery is needed" bullpen recover funds --dry-run --output json Follow the plan. It may move proven legacy Deposit Wallet assets locally or direct you to support.
"Wallet records disagree" bullpen recover polymarket --refresh --output json Follow recovery_plan.next_action; include doctor wallet JSON only when the plan or support asks for it.

For trading-wallet rejections, you are done when bullpen fix --refresh reports no blocking action and your original command succeeds.

For wallet-routing disagreement, you are done when bullpen recover polymarket --refresh --output json reports no blocking action and your original command succeeds.

For missing deposits, you are done when you have created a bullpen support bundle --output json artifact or attached the read-only diagnostic evidence to a Bullpen Support ticket, unless the CLI prints a self-service recovery plan whose source, destination, route, and asset details match the deposit you are trying to recover.

Self-Serve Versus Escalate

  • Self-serve: a clean "assets on an owned wallet" result is normal only when bullpen recover funds --dry-run --output json says the CLI can move the funds locally, the account ownership evidence is strong, and the source, destination, route, and asset details match your intent. Execute only if the JSON plan is correct. See Preview Recovery Workflows.
  • Escalate to support: wrong-chain or wrong-token deposits, exchange/imported wallet destinations, trading-wallet or order-route rejections after bullpen fix --refresh, routing disagreements after bullpen recover polymarket --refresh --output json, routing-only address paths, and wallet-record disagreements are not direct self-serve movements. If the plan says local recovery is unavailable or uncertain, stop and create a support artifact with bullpen support bundle --output json; do not force a wallet kind to "fix" them.

Before You Start

  • Run diagnostics from the same Bullpen account, machine, and environment that saw the issue.
  • Do not delete ~/.bullpen, export private keys, bridge manually, or force a wallet kind as the first recovery step.
  • Do not share private keys, seed phrases, JWTs, API tokens, full config files, or unredacted logs with support.
  • Treat --non-interactive and --yes as separate controls. Non-interactive mode does not approve money-moving commands.

Safe First Commands

For most recovery questions, run:

bullpen recover funds --dry-run --output json

For trading-wallet rejections, refresh the account-level fix plan:

No immediate state-change to orders or funds: Run the readiness refresh only when the failed command or support identifies a trading-wallet rejection and asks for the account-level fix plan.

bullpen fix --refresh

For wallet-routing disagreement failures, refresh the Polymarket recovery plan. This is a read-only planning command; it does not move funds:

bullpen recover polymarket --refresh --output json

If either plan sends you to support, create one privacy-contract-bearing artifact:

bullpen support bundle --output json

Use the advanced wallet audit only when the recovery output or support asks for wallet evidence. It is read-only by default and includes root privacy_contract disclosure when shared as a support artifact.

bullpen polymarket wallet-audit --refresh --output json

The pattern to look for is an asset balance on a wallet that is not selected for trading. Addresses and amounts below are redacted and illustrative; your exact fields will differ.

Example output — stranded assets on a non-selected wallet
{
  "schema_version": 1,
  "owners": [
    {
      "owner_eoa": "0xOWNER…1234",
      "server_selected": { "kind": "deposit_wallet", "address": "0xDEP…aaaa" },
      "wallets": [
        {
          "kind": "deposit_wallet",
          "address": "0xDEP…aaaa",
          "status": "active",
          "is_server_selected": true,
          "pusd": "0.00",
          "usdce": "0.00",
          "positions": 0
        },
        {
          "kind": "safe",
          "address": "0xSAFE…bbbb",
          "status": "inactive_legacy",
          "is_server_selected": false,
          "pusd": "42.10",
          "usdce": "0.00",
          "positions": 1,
          "stranded": true
        }
      ]
    }
  ],
  "summary": { "stranded_assets_found": true }
}

The audit enumerates Bullpen-managed 0x wallets, checks selected Polymarket routing, and probes Safe, Proxy, current Deposit Wallet, deployed legacy Deposit Wallet, and CLI-proven locally signable owned EOA state without submitting a transaction. See the command reference for bullpen polymarket wallet-audit; it documents --owner and --include-order-credit.

If legacy Deposit Wallet evidence appears, do not assume all recovery is impossible. Run bullpen recover funds --dry-run --output json and use the plan outcome. The plan will either say the CLI can move proven owned assets or direct you to support.

If the report is about a missing or wrong-chain deposit, run the deposit diagnostic next. It is read-only and does not attempt recovery.

bullpen funds diagnose-deposit --tx <TX_HASH> --chain <CHAIN> --token <TOKEN> --address <RECIPIENT_ADDRESS> --output json

Use the generated funds diagnose-deposit reference for accepted chain and token values. If you only know the recipient, the reference also documents the address-only diagnostic shape.

Read The Result

Use the JSON output as evidence, not as permission to run a repair command. Account-level recovery guidance appears in recovery_plan. Funds movement guidance uses root status and legs[]. Login-attempt guidance appears in auth_incident. Follow the fields in the object returned by the command instead of combining steps from multiple guides.

Preview or read-only commands come first in this section: use bullpen recover funds --dry-run --output json, bullpen recover polymarket --refresh --output json, or the relevant diagnostic output before any live recovery command.

Result class Next safe action
Wallet route is consistent and no stranded assets are reported Continue with the original failed command's preview or diagnostic path.
Assets or positions appear on an owned Safe, Proxy, current Deposit Wallet, deployed legacy Deposit Wallet, or CLI-proven locally signable owned EOA Preview bullpen recover funds --dry-run --output json before execution.
The recovery preview says local recovery is available Live: Execute bullpen recover funds --yes only after reviewing the dry-run JSON and confirming root status is ready, every executable leg has outcome: "supported" and effective_evidence.submit_authorized: true, and the source, destination, route, and assets match your intent.
The recovery preview says nothing needs to move Do not submit another command for that item.
The recovery preview says local recovery is unavailable or uncertain Do not force wallet selection or retry live money movement. Run the printed support command or bullpen support bundle --output json.
Deposit was sent to a chain or token the CLI cannot recover, an exchange address, imported external EVM wallet, routing-only address, or bot Safe Do not try to select that wallet for trading or managed recovery. Include diagnose-deposit JSON and wallet-audit JSON in your support ticket; that support handoff is the completion condition for CLI-side work.
Wallet records or on-chain state disagree, or the error reports a wallet-routing disagreement Run bullpen recover polymarket --refresh --output json and follow recovery_plan.next_action. Include doctor wallet JSON only when the plan or support asks for passive evidence.

Run the broader wallet diagnostic when the issue is account inventory or cache disagreement rather than stranded Polymarket assets.

bullpen doctor wallet --output json

A healthy result says no disagreements were found. If the diagnostic reports a disagreement, residual funds, or an inconclusive wallet state, the local cache, Bullpen records, and on-chain bytecode do not agree for at least one Bullpen-managed 0x wallet. Run bullpen recover polymarket --refresh --output json for the recovery verdict; attach doctor wallet JSON only when the verdict or support asks for it. The safety section confirms the command performed no writes. doctor wallet is passive: it does not sign, submit, or run the extra order credit check. If support needs order-route rejection evidence, use the explicit wallet-audit --include-order-credit command or attach the original failed money command output. The command never treats a successful balance read as proof that a wallet is accepted for trading.

Example output — wallet disagreement (illustrative, redacted)
{
  "schema_version": 2,
  "status": "disagreements_found",
  "total_owners": 1,
  "entries": [
    {
      "owner": "0xOWNER…1234",
      "local_cache_kind": "legacy_safe",
      "server_selected_kind": "safe",
      "onchain_deposit_wallet_deployed": true,
      "disagreement": "server_safe_but_dw_funded",
      "hint": "Deposit Wallet is funded on-chain but server selects the Safe. Include this JSON in your support ticket; do not force a wallet kind."
    }
  ],
  "safety": {
    "signer_creation": false,
    "token_refresh": false,
    "wallet_cache_mutation": false
  }
}

See the generated doctor wallet reference.

Check Order Credit

Default wallet-audit and recovery dry-runs skip authenticated order credit checks for each wallet row. If funds still look missing after the read-only audit, or if the recovery dry-run says order credit checking was skipped or incomplete, run the explicit order credit probe before choosing an asset-movement command.

bullpen polymarket wallet-audit --refresh --include-order-credit --output json

Read-only, support-gated: The JSON summary tells you whether order credit was checked for each wallet. Order credit cannot be moved on-chain; when it exists on a wallet that is not selected for trading, support may route the case through a web-app withdrawal or support review instead of consolidate.

Preview Recovery Workflows

Preview recovery when assets or positions appear on an owned Polymarket wallet that the active trade path cannot use.

bullpen recover funds --dry-run --output json

Preview: The dry-run prints a plan without executing it. The next_action value in the JSON may name a live command, but that field is guidance for after review, not a preview action.

Live: Run bullpen recover funds --yes only when the root status is ready, every executable leg has outcome: "supported", effective_evidence.submit_authorized: true, and the source, destination, route, and assets match your intent.

Example output — funds recovery dry-run plan (illustrative, redacted)
{
  "schema_version": 1,
  "dry_run": true,
  "status": "ready",
  "operation": "recover_funds",
  "legs": [
    {
      "source_identity": {
        "owner_eoa": "0x1111111111111111111111111111111111111111",
        "source_address": "0x2222222222222222222222222222222222222222",
        "source_class": "non_selected_safe",
        "wallet_kind": "Safe",
        "server_wallet_type": "Safe",
        "server_wallet_type_raw": null,
        "deposit_wallet_derivation": null,
        "is_server_selected": false,
        "operation": "migrate_pusd",
        "asset_class": "pusd",
        "identity_fingerprint": "recovery-leg:v1:0000000000000000000000000000000000000000000000000000000000000000"
      },
      "destination_identity": {
        "destination_address": "0x3333333333333333333333333333333333333333",
        "destination_policy": "selected_or_explicit_owned_wallet",
        "wallet_kind": "DepositWallet",
        "is_explicit": false
      },
      "observed_source_rows": [
        {
          "source": "wallet_audit",
          "state": "verified_onchain",
          "reason": "pUSD balance found on a non-selected Safe"
        }
      ],
      "asset_evidence": {
        "state": "verified_onchain",
        "reason": "pUSD balance found on-chain",
        "proof_scope": "polygon_rpc",
        "amount": "42.100000",
        "amount_policy": "exact_amount"
      },
      "effective_evidence": {
        "state": "verified_onchain",
        "reason": "pUSD balance found on-chain",
        "submit_authorized": true,
        "proof_scope": "polygon_rpc"
      },
      "route": {
        "operation": "migrate_pusd",
        "asset_class": "pusd",
        "asset_identifier": "pUSD",
        "source_address": "0x2222222222222222222222222222222222222222",
        "destination_address": "0x3333333333333333333333333333333333333333",
        "destination_kind": "DepositWallet"
      },
      "outcome": "supported",
      "submit": {
        "state": "not_submitted",
        "tx_hash": null,
        "receipt_state": null,
        "verification_state": null,
        "source_delta": null,
        "destination_delta": null,
        "token_delta": null,
        "operation_proof": null,
        "safe_to_retry": false
      },
      "next_action": "Run `bullpen recover funds --yes` after reviewing this plan.",
      "safe_to_retry": false,
      "support_artifact": null
    }
  ],
  "whole_plan_preflight": {
    "state": "not_checked",
    "checked_leg_count": 0,
    "failure_reason": null
  },
  "partial_execution": {
    "completed": 0,
    "failed": 0,
    "skipped": 0,
    "not_started": 1
  }
}

Eligible recovery sources include selected and non-selected owned Safe, Proxy, current Deposit Wallet, deployed legacy Deposit Wallet, and CLI-proven locally signable owned EOA sources. Direct-address submit requires CLI-proven signing authority and sufficient POL/MATIC gas. Other address-source routing requires Bullpen support.

Recovery never changes the selected trading wallet. It moves eligible assets toward the current destination in the plan. On live submit, the CLI refreshes the source identity, destination identity, and exact transfer details. If they changed since preview, the command stops before any transfer. It also validates the full plan before the first transfer, so one leg the CLI cannot execute blocks partial execution.

Understand Recovery JSON

JSON and human output distinguish these fields:

Field Values Meaning
status Dry-run: ready, no_op, backend_only, blocked; live: complete, preflight_failed, partial_execution, failed, executed, blocked Root plan or submit status. Live execution requires dry-run root status: "ready" plus executable-leg evidence below.
legs[] Array of recovery actions The per-asset or per-route work units.
legs[].source_identity / legs[].destination_identity Address, wallet kind, selected state, and authority fields Confirm the source and destination match your intent before submitting.
legs[].route operation, asset_class, asset_identifier, source/destination addresses, and optional destination kind The exact operation the leg plans to run.
legs[].outcome supported, no_op, unsupported, backend_only, unknown Whether a plan leg can run locally, needs no action, needs support, or lacks enough evidence. Treat only supported as locally executable.
legs[].effective_evidence.submit_authorized true / false Whether ownership and asset evidence are strong enough for local submit. A ready preview should have this set to true for executable supported legs.
legs[].submit state, optional transaction fields, and live retry evidence Submit and post-submit state for each action. In dry-runs, legs[].submit.state is usually not_submitted for executable actions or skipped_no_op for no-op actions. Do not use retry fields to decide whether an ordinary preview is authorized.
legs[].submit.state not_submitted, submitted, confirmed, failed, outcome_unknown, skipped_no_op, skipped_preflight_failed, not_started Submit status for each leg.
partial_execution completed, failed, skipped, not_started Counts after a live run.
legs[].submit.safe_to_retry true / false Post-live-submit retry evidence. Retry only the submitted failed/unknown leg when that leg reports true; do not resubmit a leg with outcome_unknown or safe_to_retry: false until status or support review resolves it. Ignore this field for ordinary dry-run preview authorization because no submit has occurred.

Diagnostic and support metadata may include resolution_owner on objects such as recovery_plan, wallet readiness, order-route readiness, auth incidents, signing capability, or support bundles. Ordinary ready recovery previews do not use a top-level resolution_owner to decide whether to submit; interpret them from legs[].source_identity, legs[].destination_identity, legs[].effective_evidence.submit_authorized, legs[].outcome, legs[].submit.state, and legs[].next_action. Use legs[].submit.safe_to_retry only after a live submit, and only for the leg whose submit status failed or became unknown.

If support asks you to inspect only one Bullpen-managed 0x wallet, use the generated consolidate reference for the exact --owner form. Do not force --wallet-kind unless support explicitly directs it.

Mutating Commands

The commands in this section can submit transactions, change account metadata, or move funds. Run them only after diagnostics and plan review.

Execute recovery only after the dry-run routes eligible assets to the intended destination. Run bullpen recover funds --dry-run --output json first and review the source, destination, route, assets, and executable-leg evidence.

bullpen recover funds --dry-run --output json

Live: This command can move funds, cancel orders, transfer positions, merge complete sets, and sweep balances according to the reviewed recovery plan.

bullpen recover funds --yes

Run this only after the dry-run root status is ready, every executable leg has outcome: "supported" and effective_evidence.submit_authorized: true, and the source, destination, route, and asset details match your intent. If the dry-run says local recovery is unavailable or uncertain, stop and run bullpen support bundle --output json. The command can cancel orders, transfer positions, merge complete sets, and sweep balances according to the reviewed recovery plan. A successful command on one account does not prove another affected account is recovered.

Sweep stranded ERC-20 funds only when diagnostics and support identify a supported Polygon ERC-20 balance held by a Bullpen-owned Safe, Proxy, or Deposit Wallet.

bullpen recover funds --dry-run --output json

If support confirms sweep-stranded is the right command and gives you the exact token, source, amount, and destination, submit the reviewed transfer:

Destructive, support-directed immediate state-change: sweep-stranded has no separate dry-run flag; execute it only when the recovery preview or Bullpen support confirms the source asset, amount, and destination.

bullpen polymarket sweep-stranded \
  --token <TOKEN> \
  --from <SOURCE_WALLET> \
  --to <DESTINATION_WALLET> \
  --amount <AMOUNT>

The form above performs preflight checks, prints the transfer summary, and asks for confirmation. Run it only after the recovery preview or Bullpen support confirms the source asset, amount, and destination. This is a one-way transfer. Review the generated sweep-stranded reference before confirming, especially recipient rules and the --unsafe-recipient boundary.

Deploy or re-register a Deposit Wallet only when diagnostics show that the selected trading route is Deposit Wallet and the generated command output says that action is appropriate.

bullpen polymarket wallet-audit --refresh --output json

After reviewing the diagnostic output or support instruction, run only one specific confirmed command. To deploy a selected Deposit Wallet:

Write, support-gated immediate state-change: There is no separate preview flag; deploy only when diagnostics or support identify this exact repair and the command's confirmation flow shows the selected Deposit Wallet route.

bullpen polymarket deploy-deposit-wallet

To refresh registration only when diagnostics or support identify that exact repair:

Write, support-gated immediate state-change: There is no separate preview flag; re-register only when diagnostics or support identify this exact repair and the command's confirmation flow shows the selected Deposit Wallet route.

bullpen polymarket reregister-deposit-wallet

These commands do not override normal wallet selection. They have no separate --preview flag; without --yes, they open an interactive confirmation flow after checking current state. --force-relayer-refresh is a support-directed compatibility override; do not use it after an external registration rejection unless Bullpen support gives that exact instruction. Current builds treat that rejection as terminal for local repair and generate a support bundle instead. Do not use this command when the CLI says funds are at a legacy pre-beacon address pending recovery; that state is different from a current Deposit Wallet registration refresh. Bullpen support must confirm external repair before approval, activation, or trading resumes. See the generated deploy-deposit-wallet reference and reregister-deposit-wallet reference.

Select a Bullpen-managed 0x wallet only when you intend to change the primary wallet for a chain.

Write, immediate state-change: Execute wallet selection only when you intend to change the primary EVM wallet to the exact Bullpen-managed address shown by bullpen wallet list or bullpen wallet addresses.

bullpen wallet select --chain evm <BULLPEN_0X_WALLET>

This selects a Bullpen-managed 0x wallet; it does not directly choose the selected Polymarket trading wallet. The address must be a Bullpen-managed EVM wallet; imported external EVM wallets remain visible but cannot be selected. See the generated wallet select reference.

Support Capture

Create one privacy-contract-bearing support artifact:

bullpen support bundle --output json

The command collects the CLI version, session health, and selected trading wallet evidence without exposing credentials. Its root privacy_contract discloses whether operational wallet/account/resource, condition, transaction, order, or trade IDs, exact balances, and local paths are included. Include the failed command's short error message in the ticket. Run individual JSON diagnostics only when support asks for a specific follow-up.

Do not attach secrets or full unredacted logs. Raw logs are exceptional evidence; attach them only when support asks after JSON artifacts are insufficient, and manually scrub credentials, tokens, private keys, auth headers, raw provider bodies, raw credential error chains, and unrelated local paths first.

See Also