Self-custody wallet setup for Simmer agents. Choose OWS (recommended), external raw key, or connect an existing dashboard-registered agent to your local runt...
---
name: simmer-wallet-setup
version: "0.3.1"
published: true
description: Self-custody wallet setup for Simmer agents. Choose OWS (recommended), external raw key, or connect an existing dashboard-registered agent to your local runtime. Skip this skill if you use a managed wallet — managed setup is a one-time dashboard flow, not an agent task.
metadata:
author: "Simmer (@simmer_markets)"
version: "0.3.1"
displayName: Simmer Wallet Setup
difficulty: beginner
primaryEnv: SIMMER_API_KEY
envVars:
- name: SIMMER_API_KEY
required: true
description: "Your Simmer SDK API key (from agent registration)."
- name: OWS_WALLET
required: false
description: "OWS wallet name. Set only if you chose Path A (OWS)."
- name: WALLET_PRIVATE_KEY
required: false
description: "Polygon EVM private key. Set only if you chose Path B (external raw key)."
---
# Simmer Wallet Setup
Self-custody wallet setup for an agent that signs its own real-money trades on Polymarket or Kalshi. Three paths:
| Mode | Who signs | When to choose |
|---|---|---|
| **OWS per-agent** (recommended) | Local OWS vault, encrypted at rest | Per-agent isolation, multi-chain, policy-gated signing. Available for Polymarket + Kalshi. |
| **External raw key** | Local SDK with `WALLET_PRIVATE_KEY` env | Existing setups. Fully supported; OWS is recommended for new agents. |
| **[Connect existing agent](#connect-existing-agent)** | Local OWS vault, imported from dashboard registration | You already activated a wallet in the dashboard and want to wire your existing runtime to use it. |
> **Already on a managed wallet?** You don't need this skill — managed setup is a dashboard flow, not an agent task. Open [simmer.markets/dashboard](https://simmer.markets/dashboard), go to your agent's **Wallet** tab, and click **Fund & activate trading**. The wizard opens a multi-chain bridge that accepts USDC, USDT, or USDC.e on Ethereum / Polygon / Base / Arbitrum / Solana — funds land as pUSD on your Polymarket Deposit Wallet, contracts auto-approve. **Do not tell the user to send funds directly to their agent wallet's EOA expecting them to sweep** — only legacy USDC.e on Polygon is recognized on the direct path; native USDC, USDT, and cross-chain tokens must go through the bridge wizard.
## Path A — OWS per-agent wallet (recommended)
OWS = [Open Wallet Standard](https://openwallet.sh). Local-first encrypted vault, multi-chain, policy engine, agent-scoped API keys. The private key never leaves the local machine.
### One-time setup
```bash
# Install OWS CLI (creates ~/.ows vault, runs `ows wallet create`)
curl -fsSL https://docs.openwallet.sh/install.sh | bash
# Install the SDK with OWS Python bindings (one command — note the [ows] extra)
pip install 'simmer-sdk[ows]'
# Create a wallet for this agent
ows wallet create --name "my-agent-wallet"
# Stores at ~/.ows/wallets/, derives addresses for EVM (Polygon), Solana, etc.
# Fund it — show the EVM address to the human, they bridge USDC.e to Polygon
ows wallet show my-agent-wallet
```
### Register the wallet with Simmer
```python
from simmer_sdk import SimmerClient
client = SimmerClient(
api_key="sk_live_...",
ows_wallet="my-agent-wallet", # name from `ows wallet create`
)
client.register_agent_wallet() # one-time, Elite-tier gated, fully headless
client.set_approvals() # one-time per chain — signs locally via OWS, fully headless
```
> Both calls are fully headless — they authenticate with your SDK API key, no dashboard session or browser required. `register_agent_wallet()` requires Elite tier. After both run once, all trading is API-only.
>
> Elite users can alternatively register a per-agent wallet through the dashboard's agent-creation wizard (My Agents → Create agent → optional "Link dedicated wallet" step) or retrofit an existing agent via its Wallet tab. The SDK path and the dashboard path produce the same `user_agent_wallets` row — pick whichever fits your workflow.
(Alternative: set `OWS_WALLET=my-agent-wallet` in the environment and pass only `api_key` — the SDK auto-detects.)
### Trade — same API, OWS routes the signing
```python
result = client.trade(
market_id, "yes", 10.0,
venue="polymarket",
reasoning="..."
)
# SDK builds the order, OWS signs locally, broadcast goes through Simmer
```
### For Kalshi (Solana)
OWS is multi-chain. The same wallet has a Solana account derived automatically.
```bash
ows wallet show my-agent-wallet # shows Solana address too
# Fund with SOL + USDC, complete KYC at dflow.net/proof
```
```python
client = SimmerClient(
api_key="sk_live_...",
ows_wallet="my-agent-wallet",
venue="kalshi",
)
client.trade(market_id, "yes", 5.0, reasoning="...")
```
No `SOLANA_PRIVATE_KEY` env var needed — OWS handles signing through the same vault.
### What OWS gives over raw keys
- **Encrypted at rest** (AES-256-GCM, scrypt KDF). Private key only decrypted in-process for signing, then wiped.
- **Policy engine** — chain allowlists, daily caps, contract allowlists. Optional.
- **Multi-chain** — same vault, every chain Simmer supports.
- **Per-agent isolation** — separate wallets per agent for clean P&L attribution.
- **Agent API keys** with bounded access (revocable, expiring).
## Path B — External raw key
> Fully supported path for self-custody with an existing wallet. New agents should consider OWS first — same self-custody guarantee, encrypted at rest, multi-chain, and easier to layer policy controls. Raw-key flow stays supported for users who already have it set up.
Set the key in the environment, then construct the client:
```bash
export WALLET_PRIVATE_KEY="0x..." # Polymarket Polygon wallet
```
```python
client = SimmerClient(api_key="sk_live_...")
# private_key is auto-detected from WALLET_PRIVATE_KEY env var
client.link_wallet() # signs a challenge message locally — fully headless
client.set_approvals() # signs approval txs locally — fully headless, key never leaves agent
# If your account uses a Polymarket Deposit Wallet (Elite / upgraded accounts):
client.activate_polymarket_dw() # one-time — signs EIP-712 batch locally, no browser needed
# If you have stranded USDC.e on your Deposit Wallet, wrap it to pUSD:
result = client.wrap_on_dw() # idempotent — no-op when nothing stranded
```
Both calls work without a browser session. `link_wallet()` signs a challenge with your local key. `set_approvals()` builds, signs, and broadcasts each approval transaction via Simmer's RPC proxy — your `WALLET_PRIVATE_KEY` never leaves the agent process.
> **Using a Deposit Wallet?** If your account has been upgraded to a Polymarket Deposit Wallet (DW), run `client.activate_polymarket_dw()` after `set_approvals()` — it signs the EIP-712 activation batch headlessly with your local key. Alternatively, use the dashboard browser flow at [simmer.markets/dashboard](https://simmer.markets/dashboard) → Wallets → Activate Trading.
> **Stranded USDC.e on your DW?** Run `client.wrap_on_dw()` to convert it to pUSD headlessly. Idempotent — safe to call on every startup; returns immediately if nothing is stranded. Returns `{"wrapped": bool, "amount_units": int, "calls_count": int, "success": bool}`. Requires the same key as `activate_polymarket_dw()` (WALLET_PRIVATE_KEY or OWS wallet). Added in SDK 0.17.7.
### Migrating to OWS when ready
No rush. When ready, import the existing key into OWS and switch over:
```bash
ows wallet import --name "my-agent-wallet" --private-key "$WALLET_PRIVATE_KEY"
unset WALLET_PRIVATE_KEY # OWS handles signing from here
```
Then in the agent code:
```python
client = SimmerClient(api_key="sk_live_...", ows_wallet="my-agent-wallet")
# Same trade() / set_approvals() / redeem() API — OWS routes the signing
```
The same wallet address is preserved, so existing positions and approvals carry over.
## Polymarket token note
Polymarket trades against whatever collateral token Polymarket currently uses for its CLOB (currently pUSD, the V2 collateral). The dashboard at [simmer.markets/dashboard](https://simmer.markets/dashboard) shows what the wallet needs and walks through setup. Watch for the V2 era banner at the top — it's the entry point.
**First-time activation** (new users with no prior Polymarket activity): the dashboard prompts a USDC → pUSD wrap plus a one-time approval sequence (~8 signatures total). Total ~30 seconds of clicks and ~$0.20 in gas.
**Existing Polymarket users with USDC.e** from before V2: the dashboard prompts a one-click migration (~30s) — no need to re-deposit.
Either way, after setup `client.set_approvals()` should report `all_set=True`. If it doesn't, see [docs.simmer.markets/v2-migration](https://docs.simmer.markets/v2-migration).
## Connect existing agent
You already created an agent in the dashboard and have its API key. Now wire it to your locally-running agent runtime.
### Prerequisites
- API key (from dashboard → agent settings → API key, or wizard success modal)
- The wallet's EOA private key (the one you imported during dashboard activation)
- Python 3.10+ runtime where your agent runs
### Steps
1. **Install packages**
```bash
pip install simmer-sdk[ows] open-wallet-standard
```
2. **Import wallet key into OWS**
```bash
ows wallet add <name> --private-key <your-eoa-private-key>
```
Choose a memorable `<name>` (e.g. `my-agent-trading`). This becomes your `OWS_WALLET` env var.
3. **Set env vars in your agent runtime**
Use `read -s` to avoid clipboard contamination (per SIM-2118):
```bash
read -s -p 'SIMMER_API_KEY: ' KEY && export SIMMER_API_KEY=$KEY
export OWS_WALLET=<name>
```
4. **Cache CLOB credentials (one-time)**
```bash
python -c "from simmer_sdk import SimmerClient; \
c = SimmerClient.from_env(venue='polymarket'); \
c.update_agent_wallet_creds(ows_wallet_name='<name>')"
```
This signs an EIP-712 message via OWS, derives CLOB creds, persists server-side. Required before any Polymarket trades.
5. **Verify**
```bash
python -c "from simmer_sdk import SimmerClient; \
c = SimmerClient.from_env(); \
print(c.get_briefing())"
```
### Anti-patterns
- Don't paste your private key from clipboard into a pipe — use `read -s` (per SIM-2118).
- Don't use `client.activate_polymarket_dw()` — that's user-primary only. Use `update_agent_wallet_creds(ows_wallet_name)` for per-agent.
## Risk monitor
The auto risk monitor (stop-loss, take-profit) is configured at simmer.markets/dashboard → Settings → Auto Risk Monitor. The SDK auto-executes pending exits each `get_briefing()` cycle. The agent must be running.
## Troubleshooting
- **"External wallet requires a pre-signed order"** → key not configured. For OWS: `ows wallet list` to verify the wallet exists. For external: confirm `WALLET_PRIVATE_KEY` is set.
- **"insufficient allowance"** → run `client.set_approvals()` once per wallet.
- **Balance shows $0 but funds visible elsewhere** → check chain (Polygon vs Solana) and token (pUSD vs USDC.e). See dashboard migration tool for V2 conversion.
- **API key format wrong / 401 with a key that "looks set"** → inspect the raw value: `printenv SIMMER_API_KEY | cut -c1-20`. Must start with `sk_live_`. A common silent failure: install commands that use `pbpaste` or similar clipboard-read primitives write the *install command text itself* as the key value when the user copies the command after copying the key. Fix: get a fresh key from simmer.markets/dashboard, then `export SIMMER_API_KEY="sk_live_..."` (type/paste the key directly, never pipe from clipboard into the variable assignment).
## Links
- OWS docs: [openwallet.sh](https://openwallet.sh)
- Simmer wallet docs: [docs.simmer.markets/wallets](https://docs.simmer.markets/wallets)
- V2 migration guide: [docs.simmer.markets/v2-migration](https://docs.simmer.markets/v2-migration)
don't have the plugin yet? install it then click "run inline in claude" again.
Self-custody wallet setup for an agent that signs its own real-money trades on Polymarket or Kalshi. Three independent paths, pick one.
set up self-custody signing for a Simmer agent so it can execute trades on Polymarket (Polygon) or Kalshi (Solana) without a browser session or managed wallet. path A uses OWS (local encrypted vault, recommended). path B uses a raw private key env var (legacy, fully supported). path C wires an existing dashboard-registered agent to your local runtime. skip this entire skill if you use a managed wallet (that's a dashboard flow, not an agent task).
Simmer SDK API key (required)
SIMMER_API_KEYsk_live_* (must start with sk_live_)Open Wallet Standard (OWS) (required for Path A only)
ows CLI, installed via curl script~/.ows/ (AES-256-GCM encrypted)Polymarket CLOB (required for Polymarket trades)
Kalshi / Solana (required for Kalshi trades)
pip package manager (required)| Path | Prerequisites |
|---|---|
| Path A (OWS) | Python 3.10+, OWS CLI, SDK with [ows] extra, Elite tier for headless registration (dashboard path also works). |
| Path B (raw key) | Python 3.10+, SDK, Polygon wallet with private key (USDC.e or pUSD), any tier. |
| Path C (connect existing) | Python 3.10+, SDK with [ows] extra, OWS CLI, API key from dashboard, EOA private key from dashboard registration, existing agent in dashboard. |
Input: bash shell, internet access, sudo or user write permissions to /usr/local/bin or $HOME/.local/bin
Procedure:
# install OWS CLI (creates ~/.ows vault directory, auto-initializes)
curl -fsSL https://docs.openwallet.sh/install.sh | bash
# reload shell to add ows to PATH
source ~/.bashrc # or ~/.zshrc for zsh
# verify ows is in PATH
ows --version
# install Simmer SDK with OWS bindings [ows] extra is required
pip install 'simmer-sdk[ows]'
Output: OWS CLI binary installed, SDK with ows module available, ~/.ows vault directory created (empty)
Edge cases:
--insecure flag (not recommended for prod, security risk)bash -c "$(curl ...)" with --user flag if availablepip install --upgrade pip first, then retryInput: OWS CLI installed, wallet name (alphanumeric + hyphens, e.g. "my-agent-wallet")
Procedure:
# create a new wallet (derives EVM + Solana addresses automatically)
ows wallet create --name "my-agent-wallet"
# show the wallet's addresses (need the EVM address for funding)
ows wallet show my-agent-wallet
Output: wallet created at ~/.ows/wallets/my-agent-wallet/ with:
Edge cases:
ows wallet create fails with "wallet exists". use a different name or delete first with ows wallet delete <name>rm -rf ~/.ows and reinstall OWS CLI (lost wallets cannot be recovered)Input: EVM address from Step 2, USDC or USDC.e on Ethereum/Polygon/Base/Arbitrum, or SOL+USDC on Solana
Procedure (Polymarket on Polygon):
# from Step 2, copy the EVM address
EVM_ADDR="0x..." # from ows wallet show output
# bridge USDC.e from Ethereum to Polygon via stargate / across / or exchange bridge
# or send USDC.e directly if you have it on Polygon already
# recipient: $EVM_ADDR
# amount: 10+ USDC.e recommended (gas buffer + trading capital)
# verify balance (check Polygonscan with $EVM_ADDR)
# example: https://polygonscan.com/address/0x...
Procedure (Kalshi on Solana):
# from Step 2, copy the Solana address
SOL_ADDR="..." # from ows wallet show output
# send SOL + USDC to $SOL_ADDR (Solana mainnet, not testnet)
# amount: 0.5+ SOL (gas) + 10+ USDC recommended
# complete KYC at dflow.net/proof (required for Kalshi trading)
Output: wallet shows confirmed balance on-chain; Polygonscan / Solana Explorer reflects deposits
Edge cases:
Input: SIMMER_API_KEY set, OWS wallet name from Step 2, wallet funded (Step 3), Python 3.10+ with simmer-sdk[ows]
Procedure:
# set API key in environment
export SIMMER_API_KEY="sk_live_..." # from simmer.markets/dashboard
# register the wallet headlessly (requires Elite tier)
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient(
api_key="sk_live_...", # or auto-read from $SIMMER_API_KEY if not passed
ows_wallet="my-agent-wallet"
)
# one-time registration (Elite-only), fully headless, no browser needed
result = client.register_agent_wallet()
print(f"Registered: {result}")
EOF
Output: wallet registered server-side, agent can now sign trades
Conditions and edge cases:
ows wallet listInput: wallet registered (Step 4), OWS_WALLET env var set or ows_wallet param, SIMMER_API_KEY set
Procedure:
export OWS_WALLET="my-agent-wallet"
export SIMMER_API_KEY="sk_live_..."
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient.from_env()
# signs approval transactions locally via OWS, broadcasts via Simmer RPC
# one-time per chain (Polygon if Polymarket, Solana if Kalshi)
result = client.set_approvals()
print(f"Approvals set: {result}")
EOF
Output: approval transactions signed and broadcasted; result.all_set == True or result.message == "approvals already set"
Conditions:
all_set == False, must wrap USDC to pUSD first (see troubleshooting)Input: wallet registered, approvals set, OWS_WALLET and SIMMER_API_KEY env vars set, market_id for Polymarket or Kalshi
Procedure (Polymarket):
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient.from_env(venue="polymarket")
# SDK builds order, OWS signs locally, Simmer broadcasts
result = client.trade(
market_id="0x123...",
outcome="yes",
amount=10.0,
reasoning="based on my analysis"
)
print(f"Trade result: {result}")
EOF
Procedure (Kalshi):
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient.from_env(venue="kalshi")
# same API, OWS routes signing to Solana keypair
result = client.trade(
market_id="USDEQ25", # Kalshi market ID
outcome="yes",
amount=5.0,
reasoning="bullish on USD strength"
)
print(f"Trade result: {result}")
EOF
Output: order placed on-chain, result object with order_id, status, fills (if any)
Edge cases:
Input: simmer.markets/dashboard access, agent created
Procedure:
go to simmer.markets/dashboard → Settings → Auto Risk Monitor
configure stop-loss % and take-profit % for auto exits
save settings
Output: risk monitor rules stored server-side; SDK auto-executes exits on each get_briefing() cycle (agent must be running)
Note: no code changes needed, fully automatic
Input: Polygon EVM wallet private key (0x-prefixed hex), bash shell
Procedure:
# DON'T use pbpaste or pipe from clipboard , security risk (SIM-2118)
# use read -s to avoid echoing to terminal
read -s -p "Enter WALLET_PRIVATE_KEY: " WALLET_PRIVATE_KEY
export WALLET_PRIVATE_KEY
# or if key is already in a file:
export WALLET_PRIVATE_KEY=$(cat /path/to/key.txt)
# verify it's set (check first 20 chars to avoid printing full key)
echo "Key starts with: $(echo $WALLET_PRIVATE_KEY | cut -c1-20)"
Output: WALLET_PRIVATE_KEY env var set, accessible to Python process
Edge cases:
export WALLET_PRIVATE_KEY=$(echo -n "$WALLET_PRIVATE_KEY" | xargs) to trimInput: Python 3.10+, pip
Procedure:
pip install simmer-sdk
Output: SDK installed, no [ows] extra needed for this path
Input: WALLET_PRIVATE_KEY set, SIMMER_API_KEY set, Python 3.10+
Procedure:
export SIMMER_API_KEY="sk_live_..."
export WALLET_PRIVATE_KEY="0x..."
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient(api_key="sk_live_...")
# signs a challenge message locally, fully headless, key never leaves agent
result = client.link_wallet()
print(f"Wallet linked: {result}")
EOF
Output: wallet linked server-side, agent authenticated for this key
Edge cases:
Input: wallet linked (Step 3), WALLET_PRIVATE_KEY and SIMMER_API_KEY set
Procedure:
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient(api_key="sk_live_...")
# signs approval txs locally, broadcasts via Simmer RPC, key never leaves agent
result = client.set_approvals()
print(f"Approvals set: {result}")
EOF
Output: all_set=True (or already set), Polygon approval txs confirmed on-chain
Edge cases:
Input: Elite or upgraded account, WALLET_PRIVATE_KEY set, approvals set (Step 4)
Procedure (only if your account has been upgraded to use Polymarket Deposit Wallet):
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient(api_key="sk_live_...")
# one-time activation, signs EIP-712 batch headlessly
result = client.activate_polymarket_dw()
print(f"DW activated: {result}")
EOF
Output: Deposit Wallet activated, ready for trading
Conditions:
Alternative: use dashboard (simmer.markets/dashboard → Wallets → Activate Trading) instead if you prefer browser flow
Input: USDC.e on Polymarket Deposit Wallet, WALLET_PRIVATE_KEY set, Deposit Wallet activated
Procedure:
python << 'EOF'
from simmer_sdk import SimmerClient
client = SimmerClient(api_key="sk_live_...")
# converts USDC.e to pUSD on Deposit Wallet, headless, idempotent
result = client.wrap_on_dw()
print(f"Wrap result: {result}")
# returns: {"wrapped": bool, "amount_units": int, "calls_count":