Skip to main content
A user connects an EOA (e.g. MetaMask), but their Polymarket conditional tokens and Robin vault shares never live in that EOA. They live in the user’s Polymarket on-chain account, a separate smart-contract wallet the EOA controls. Resolve that account once, then key every per-user read and write on it.
The EOA is only a signer. It holds no conditional tokens and no vault shares. If you query balances, positions, or vault state against the raw EOA you will get zero. Always use the resolved Polymarket account address.

Two account kinds

Every Polymarket user controls exactly one of two on-chain account types, depending on when their Polymarket account was created.
A deployed DepositWallet always takes priority. Only fall back to the Safe proxy if no DepositWallet is deployed for the EOA.

Resolving the wallet

Resolution is deterministic from the EOA — both addresses are derivable off-chain, then you confirm which is actually deployed on-chain.
1

Derive and check the DepositWallet

Derive the EOA’s DepositWallet address through the Polymarket relay client (deriveDepositWalletAddress), then ask the relay client whether it is deployed (getDeployed(address, "WALLET")). Both calls are read-only and prompt no signature.
2

Fall back to the Safe proxy

If no DepositWallet is deployed, compute the Safe proxy address from Polymarket’s Safe proxy factory (computeProxyAddress(eoa)) and check that the address has bytecode on-chain (getCode returns more than 0x).
3

Handle the unresolved case

If neither is deployed, the EOA has no Polymarket account yet. The user must transact on Polymarket once to deploy their wallet.

Transaction transport per kind

The resolved kind also decides how you submit deposits and withdrawals. Both kinds support deposit and withdraw, only the transport and the available deposit flow differ.
A DepositWallet can only push. The Polymarket relayer blocks approve (and setApprovalForAll), so the pull deposit path is impossible — you must use the single ConditionalTokens.safeBatchTransferFrom push into the vault. The Safe proxy has no such restriction and can do either. See Deposits for both flows.
Both transports accept the same shape of batched call ({ to, value, data }), so you can build the call list once and dispatch it through whichever transport the resolved kind requires.
Robin does not sponsor gas

Reference implementation

The runnable resolveWallet() and executeViaWallet() helpers live in src/wallet.ts of the robin-markets-integration example repo. They cover both kinds end to end — DepositWallet derivation, the deployment checks, the Safe protocol-kit path, and the relayer batch.

Next steps

Deposits

Build pull and push deposits for the wallet kind you resolved.

Withdrawals

Burn shares and reclaim outcome tokens plus organic yield.