# Authentication & Wallet Setup

The single most common bug for new Polymarket bot builders is the **proxy/funder address mismatch**. This file is dedicated to making sure that doesn't happen.

## The two-tier auth model

Polymarket separates **identity** (who owns the wallet) from **trading** (who is allowed to place orders).

- **L1 — wallet signature.** Your Polygon private key signs an EIP-712 message. This proves you own the wallet. Used only to derive or recover L2 credentials.
- **L2 — HMAC API credentials.** A triple `(api_key, api_secret, api_passphrase)` derived from L1. Used in the header of every order/cancel request.

The SDK does this for you:

```python
from py_clob_client.client import ClobClient

client = ClobClient(host=HOST, chain_id=137, key=PRIVATE_KEY,
                   signature_type=2, funder=FUNDER)
client.set_api_creds(client.create_or_derive_api_creds())
# done — bot can trade now
```

`create_or_derive_api_creds()` is idempotent. First call generates them and registers them with Polymarket; subsequent calls fetch the existing ones via L1 sig. Safe to call on every bot start.

## Signature types — the critical part

The `signature_type` parameter tells the CLOB **how to verify your signatures**. It must match the kind of wallet that holds your funds. Mismatch → orders silently fail or hit "address not authorized" errors.

| Value | Wallet type | Funds held by |
|---|---|---|
| `0` (default) | EOA (Externally Owned Account) | The address derived from your private key. MetaMask, Ledger, hardware wallets, raw private keys. |
| `1` | Email/Magic wallet (delegated signing) | A proxy address managed by Magic.link. Used when you signed up to Polymarket with email. |
| `2` | Browser wallet proxy / Gnosis Safe | A smart contract proxy owned by your EOA. This is what Polymarket's web UI creates for many users. |

### How to know which one you need

1. **Did you create your Polymarket account with email/Google/Apple SSO?** → `signature_type=1`. The funder is the proxy Magic created for you. Find it: Polymarket UI → Profile Settings → wallet address shown below your name. **Not** the EOA derived from your exported private key — those are different addresses.

2. **Did you connect with MetaMask / WalletConnect / a hardware wallet** *and* deposit USDC directly to the address you control? → `signature_type=0`. Funder is the same EOA. You also need to set token allowances (see below).

3. **Did you connect with MetaMask but Polymarket's UI created a proxy/smart wallet for you (Gnosis Safe)?** → `signature_type=2`. Funder is the Gnosis Safe address. Same allowance caveat.

The way to verify: on the Polymarket website, check the deposit address. If it differs from the address derived from your private key, you have a proxy and need `signature_type` 1 or 2 with `funder=` set to the deposit address.

## The funder address trap

This is the bug that costs people hours.

The `polymarket wallet create` CLI command (and the SDK with `signature_type=0`) generate an EOA wallet. If you then **deposit funds via polymarket.com** while logged in with the same wallet, the website creates a *different* proxy address (Gnosis Safe) and routes your USDC there.

Your CLI / bot uses signature type 0 → looks at the EOA address → sees zero balance → cannot trade.

**Fix:** use `signature_type=2` with `funder=<the proxy address shown in the website's deposit screen>`. Don't rely on the SDK to figure it out.

You can confirm by running:

```python
print("EOA address:", client.get_address())            # from private key
print("Funder used:", client.builder.funder_address)   # what orders sign for
```

These should match the Polymarket UI deposit address.

## Setting token allowances (EOA only)

If you use `signature_type=0` (real EOA), you need to give Polymarket's exchange contracts permission to move your USDC.e and conditional tokens. Email/Magic and Gnosis users — allowances are handled automatically.

Required approvals for an EOA wallet:

- USDC.e (`0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174`) → CLOB Exchange (`0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E` or current address per docs)
- Conditional Tokens Framework (`0x4D97DCd97eC945f40cF65F87097ACe5EA0476045`) → CLOB Exchange (set approval for all)
- USDC.e → NegRisk Adapter (if trading NegRisk markets)
- CTF → NegRisk Adapter (if trading NegRisk markets)

Use the official script:

```bash
git clone https://github.com/Polymarket/py-clob-client
cd py-clob-client
pip install web3==7.12.1
python examples/set_allowances.py
```

Or `nautilus_trader/adapters/polymarket/scripts/set_allowances.py` — adapted from a poly-rodr gist, batches all the approvals. **Run once per EOA wallet.** Costs a few cents of POL gas.

## Required `.env` template

```
# Polygon RPC — recommend QuickNode for production
RPC_URL=https://polygon-mainnet.quiknode.pro/YOUR_KEY

# Wallet — never commit
POLYMARKET_PRIVATE_KEY=0x...
POLYMARKET_FUNDER_ADDRESS=0x...    # proxy address from the UI deposit screen
POLYMARKET_SIGNATURE_TYPE=2        # 0 / 1 / 2 — see this file

# CLOB API — auto-generated on first run, paste back here
CLOB_API_KEY=
CLOB_API_SECRET=
CLOB_API_PASSPHRASE=

# Bot config
BANKROLL_USD=500
MAX_TRADE_SIZE_USD=25
MAX_DAILY_LOSS_USD=50
DRY_RUN=true                       # MUST start true

# Optional — strategy-specific keys
ANTHROPIC_API_KEY=                 # AI/LLM bot
DISCORD_WEBHOOK_URL=               # whale tracker
TELEGRAM_BOT_TOKEN=                # whale tracker
TELEGRAM_CHAT_ID=                  # whale tracker
TARGET_WALLETS=0x...,0x...         # copy trading
```

## First-run checklist for the user

After they fill `.env`, the bot's first `python main.py` run should print, in order:

1. ✅ Connecting to Polygon RPC … OK
2. ✅ CLOB connectivity (`get_ok`) … OK
3. ✅ Funder address `0x...` resolved
4. ✅ Wallet balance: $X USDC.e
5. ⚠ DRY-RUN mode — no real orders will be placed
6. ▶ Starting strategy: `<name>`

If any of (1)-(4) fail, the bot exits with a specific error message that points at the right `.env` variable.

## "API credentials already exist" — what to do

`create_or_derive_api_creds()` will recover credentials that were previously generated for the same wallet — you don't need to do anything. The function returns the same triple every time for a given (private_key, funder, signature_type). Save them to `.env` once for convenience but it's not required.

If you regenerate (rotate keys), you'll get a new triple. Old orders signed with the old triple still go through; new ones use the new triple. There's no "revoke" call for L2 creds.

## Watch-only / read-only API keys

`py-clob-client` v0.34.6 added a "readonly" API key concept — credentials that can read your account state but not place orders. Useful if you want a dashboard process separate from the trading process; the dashboard gets readonly creds. Same SDK call, different scope. See the v0.34.6 release notes.

## Multi-wallet operation

Running multiple wallets is fine, but each wallet needs its own client instance and its own L2 creds. Don't share an SDK client across wallets — the L2 sig is wallet-specific. For copy-trading multiple users out of one operator wallet, use one operator wallet with multiple `(target_wallet, copy_ratio)` pairs in config; don't try to mirror through different sub-wallets unless you have a strong reason.
