Deposit Wallet Lifecycle
The deposit wallet has a defined lifecycle. Polygolem handles every phase, and go-bot’s live loop automates the full startup sequence so a restart always recovers to a ready state.
Lifecycle Phases
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ DERIVE │ ──→ │ DEPLOY │ ──→ │ APPROVE │ ──→ │ FUND │ │ (offline)│ │ (relayer)│ │ (relayer)│ │ (onchain)│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ └───────────────────────────────────────────────────▼ ┌──────────┐ ┌──────────┐ │ TRADE │ ──→ │ REDEEM │ │ (CLOB) │ │ (relayer)│ └──────────┘ └──────────┘Phase 1: Derive (offline, always succeeds)
The deposit wallet address is computed from the EOA address using CREATE2. No network calls. The same EOA always produces the same address.
wallet = CreateAddress2( factory = 0x00000000000Fb5C9ADea0298D729A0CB3823Cc07, salt = keccak256(abi.encode(factory, LeftPad32(EOA))), hash = keccak256(proxyInitCode))Phase 2: Deploy (relayer, idempotent)
The relayer deploys a clone proxy via the deposit wallet factory and funds the deployment gas. The go-bot checks deployment status before submitting.
WALLET-CREATE { from: <EOA>, to: <factory> }On-chain code is the source of truth. The relayer’s
/deployedendpoint can returnfalseeven when the deposit wallet is fully deployed on Polygon (for example, after a staleWALLET-CREATErow markedSTATE_FAILEDdespite the underlying transaction landing). Polygolem and the go-bot SDK both fall back toeth_getCodeat the derived address; if bytecode exists, the wallet is treated as deployed andWALLET-CREATEis skipped.polygolem deposit-wallet status --jsonexposes which path answered viarelayerDeployed,onchainCodeDeployed, anddeploymentStatusSource.
Phase 3: Fund (onchain)
pUSD must be transferred from the EOA to the deposit wallet. The EOA’s pUSD balance does not count for deposit wallet trading. Only pUSD held by the deposit wallet contract is available for CLOB orders.
Two-step funding:
- EOA acquires pUSD (bridge POL or swap)
- EOA transfers pUSD to deposit wallet (ERC-20 transfer)
Phase 4: Approve (relayer WALLET batches)
The deposit wallet must approve trading contracts before CLOB orders can settle. That trading readiness batch is six calls:
- pUSD
approveforCTFExchangeV2 - CTF
setApprovalForAllforCTFExchangeV2 - pUSD
approveforNegRiskExchangeV2 - CTF
setApprovalForAllforNegRiskExchangeV2 - pUSD
approveforNegRiskAdapterV2 - CTF
setApprovalForAllforNegRiskAdapterV2
V2 split, merge, and redeem readiness is separate. The collateral adapters also need a one-time WALLET batch:
- pUSD
approveforCtfCollateralAdapter - CTF
setApprovalForAllforCtfCollateralAdapter - pUSD
approveforNegRiskCtfCollateralAdapter - CTF
setApprovalForAllforNegRiskCtfCollateralAdapter
Redeem itself requires the CTF approval leg; pUSD approval is included so the same adapter-readiness batch also covers future split flows.
Phase 5: Trade (CLOB)
With the wallet deployed, funded, and approved, orders are placed with
deposit-wallet signing automatically. The EOA signs the order, but the CLOB
sees the deposit wallet as both maker and signer in the V2 order payload.
Phase 6: Redeem Winners (relayer WALLET batch)
After a market resolves, query Data API positions for the deposit wallet and
look for redeemable=true. That is the readiness signal; there is no separate
resolved boolean in the current Data API position schema.
V2 redeem routes through collateral adapters, not ConditionalTokens
directly. The owner signs an EIP-712 WALLET batch, and the relayer submits
that batch through the deposit-wallet factory:
| Market Type | Adapter |
|---|---|
| Standard binary | CtfCollateralAdapter |
| Negative-risk | NegRiskCtfCollateralAdapter |
The adapter uses conditionId, detects the wallet’s CTF balances, redeems the
winning side, wraps proceeds back into pUSD, and returns pUSD to the deposit
wallet.
Do not use direct EOA calls, raw CTF calls, or SAFE/PROXY relayer examples as fallbacks for deposit-wallet positions. If the relayer rejects the adapter-targeted WALLET batch, first verify the adapter addresses against Polymarket’s current contracts reference. If the addresses are current, surface the upstream blocker and stop.
The operator surface is polygolem deposit-wallet approve-adapters,
redeemable, and redeem. Every signing path is dry-run by default;
submission requires both --submit and a typed --confirm token
(APPROVE_ADAPTERS for adapter approvals, REDEEM_WINNERS for redeem).
See Redeeming Winning Positions for the
full runbook.
Recovery Modes
On every startup, go-bot walks the lifecycle and fills any gaps. Every step is idempotent — running it twice on an already-ready wallet is a no-op.
| State on Restart | What Happens |
|---|---|
| No deployment | IsDeployed() → false and eth_getCode empty → WALLET-CREATE → poll until mined |
| Deployed (relayer says so) | IsDeployed() → true → skip deploy |
| Deployed (relayer false-negative) | IsDeployed() → false but eth_getCode non-empty → treat as deployed, skip WALLET-CREATE |
| Deployed, no pUSD | Wallet has code, balance zero → fund EOA pUSD → transfer to wallet |
| Deployed, pUSD present, no approvals | Balance OK, allowances empty → SubmitWalletBatchSafe with fresh nonce |
| Deployed, trade approvals present, adapter approvals missing | Trading can work, but split/merge/redeem must fail closed until adapter approvals are submitted |
| Fully operational | All checks pass → skip to live trading loop |
| Nonce conflict (lost response after a previous batch) | SubmitWalletBatchSafe re-fetches nonce from relayer, signs with new nonce, retries |
| Wallet lost (never happens — it’s a contract on Polygon) | Re-derive address (same result) → on-chain code still present → skip deploy |
Nonce Safety
Every WALLET batch consumes one nonce tracked by the relayer per EOA. The nonce is never cached or reused:
- Fetch fresh nonce from
GET /nonce?address=<EOA>&type=WALLET - Sign the batch EIP-712 with that nonce
- Submit to
POST /submit(type=WALLET) - If submission succeeds → poll for confirmation
- If submission fails with nonce conflict (a previous lost response consumed it) → re-fetch (step 1) and retry
- If submission fails with any other error → report and stop
This guarantees:
- No duplicate nonce submission
- Recovery from lost responses without operator intervention
- Max 3 retries before failing with a clear error
First-Time Onboard
First-time setup has one upstream account step: create or verify the builder
profile at polymarket.com/settings?tab=builder. After that, polygolem can
derive CLOB credentials, validate relayer HMAC credentials, and run the wallet
lifecycle from the CLI.
# Derive CLOB credentials for account reads and same-address order auth.POLYMARKET_PRIVATE_KEY="0x..." \ polygolem builder auto
# Mint a V2 relayer API key for deposit-wallet deploy and approval batches.POLYMARKET_PRIVATE_KEY="0x..." \ polygolem auth headless-onboard
# Full wallet onboarding (deploy + trading approve + fund).POLYMARKET_PRIVATE_KEY="0x..." \RELAYER_API_KEY="..." \RELAYER_API_KEY_ADDRESS="..." \ polygolem deposit-wallet onboard --fund-amount 50 --jsonbuilder auto signs a ClobAuth EIP-712 message locally and posts to
clob.polymarket.com/auth/api-key. auth headless-onboard performs SIWE
login and mints the V2 relayer key used by deposit-wallet deploy and
deposit-wallet approve. Non-zero order attribution is configured separately
with POLYMARKET_BUILDER_CODE or CLI --builder-code.
This single command currently runs: derive → deploy → trading approve (6 calls) → fund (transfer). V2 redeem readiness adds the separate collateral-adapter approval batch described in Phase 4.
Then sync the CLOB balance:
POLYMARKET_PRIVATE_KEY="0x..." \ polygolem clob update-balance --asset-type collateralAutomated Startup (go-bot live)
go-bot’s live command automates the full lifecycle at startup:
go-bot live startup: 1. derive deposit wallet address (offline) 2. check IsDeployed(); if false, fall back to eth_getCode → deploy only when both say not deployed 3. check deposit wallet pUSD balance → fund if needed 4. check collateral allowances → approve if needed 5. update CLOB balance cache 6. enter live trading loop with deposit-wallet signingWith persistence, a restart after a crash recovers cleanly:
crash → restart → derive (same address) → IsDeployed or eth_getCode=true→ skip deploy → balance OK → skip fund → allowances OK → skip approve→ start tradingChecking Status
# Is the wallet deployed? (relayer + on-chain dual-source check)polygolem deposit-wallet status --json# {# "deployed": true,# "deploymentStatusSource": "polygon_code",# "relayerDeployed": false,# "onchainCodeDeployed": true,# "depositWallet": "0x21999a..."# }
# What's the deposit wallet balance?polygolem clob balance --asset-type collateral
# Check EOA onchain funds with wallet/RPC tooling; CLOB balance reads use# the deposit wallet cache.deploymentStatusSource is one of:
relayer— relayer/deployedreturned true (normal happy path).polygon_code— relayer said false, buteth_getCodereturned non-empty bytecode. The wallet is deployed; the relayer row is stale.relayer_and_polygon_code— both reported not deployed. Real first-deploy state;polygolem deposit-wallet deploy --waitwill submitWALLET-CREATE(and itself skips the call ifeth_getCodebecomes non-empty before the request is made, e.g. mid-retry).
pUSD held by the EOA and pUSD held by the deposit wallet are separate balances. Only the deposit wallet balance counts for POLY_1271 trading.
Redeem Readiness Checklist
Before submitting a V2 redeem batch:
- The wallet is deployed according to Polygon
eth_getCode. - Data API
/positions?user=<depositWallet>returns at least oneredeemable=truerow. - The position includes
conditionId,asset,outcome,outcomeIndex, andnegativeRisk. - CTF
isApprovedForAll(wallet, adapter)is true for the required adapter. - The WALLET batch is dry-run and inspected before any live submission.
go-bot must call the Polygolem SDK package directly for this flow. The Polygolem CLI is for humans and operator runbooks, not the production bot integration boundary.