409 lines
13 KiB
Markdown
409 lines
13 KiB
Markdown
> ## Documentation Index
|
||
> Fetch the complete documentation index at: https://docs.polymarket.com/llms.txt
|
||
> Use this file to discover all available pages before exploring further.
|
||
|
||
# Authentication
|
||
|
||
> Understanding authentication using Polymarket's CLOB
|
||
|
||
The CLOB uses two levels of authentication: **L1 (Private Key)** and **L2 (API Key)**.
|
||
Either can be accomplished using the CLOB client or REST API. Authentication is not
|
||
required to access client public methods and public endpoints.
|
||
|
||
## Authentication Levels
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="L1 Authentication" icon="key" href="#l1-authentication">
|
||
Use the private key of the user’s account to sign messages
|
||
</Card>
|
||
|
||
<Card title="L2 Authentication" icon="lock" href="#l2-authentication">
|
||
Use API credentials (key, secret, passphrase) to authenticate requests to the CLOB
|
||
</Card>
|
||
</CardGroup>
|
||
|
||
***
|
||
|
||
## L1 Authentication
|
||
|
||
### What is L1?
|
||
|
||
L1 authentication uses the wallet's private key to sign an EIP-712 message used in the
|
||
request header. It proves ownership and control over the private key. The private key
|
||
stays in control of the user and all trading activity remains non-custodial.
|
||
|
||
### What This Enables
|
||
|
||
Access to L1 methods that create or derive L2 authentication headers.
|
||
|
||
* Create user API credentials
|
||
* Derive existing user API credentials
|
||
* Sign/create user's orders locally
|
||
|
||
### CLOB Client
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript theme={null}
|
||
import { ClobClient } from "@polymarket/clob-client";
|
||
import { Wallet } from "ethers"; // v5.8.0
|
||
|
||
const HOST = "https://clob.polymarket.com";
|
||
const CHAIN_ID = 137; // Polygon mainnet
|
||
const signer = new Wallet(process.env.PRIVATE_KEY);
|
||
|
||
const client = new ClobClient(
|
||
HOST,
|
||
CHAIN_ID,
|
||
signer // Signer enables L1 methods
|
||
);
|
||
|
||
// Gets API key, or else creates
|
||
const apiCreds = await client.createOrDeriveApiKey();
|
||
|
||
/*
|
||
apiCreds = {
|
||
"apiKey": "550e8400-e29b-41d4-a716-446655440000",
|
||
"secret": "base64EncodedSecretString",
|
||
"passphrase": "randomPassphraseString"
|
||
}
|
||
*/
|
||
```
|
||
</Tab>
|
||
|
||
<Tab title="Python">
|
||
```python theme={null}
|
||
from py_clob_client.client import ClobClient
|
||
import os
|
||
|
||
host = "https://clob.polymarket.com"
|
||
chain_id = 137 # Polygon mainnet
|
||
private_key = os.getenv("PRIVATE_KEY")
|
||
|
||
client = ClobClient(
|
||
host=host,
|
||
chain_id=chaind_id,
|
||
key=private_key # Signer enables L1 methods
|
||
)
|
||
|
||
# Gets API key, or else creates
|
||
api_creds = await client.create_or_derive_api_key()
|
||
|
||
# api_creds = {
|
||
# "apiKey": "550e8400-e29b-41d4-a716-446655440000",
|
||
# "secret": "base64EncodedSecretString",
|
||
# "passphrase": "randomPassphraseString"
|
||
# }
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
<Warning>
|
||
**Never commit private keys to version control.** Always use environment
|
||
variables or secure key management systems.
|
||
</Warning>
|
||
|
||
***
|
||
|
||
### REST API
|
||
|
||
While we highly recommend using our provided clients to handle signing
|
||
and authentication, the following is for developers who choose NOT to
|
||
use our [Python](https://github.com/Polymarket/py-clob-client) or
|
||
[TypeScript](https://github.com/Polymarket/clob-client) clients.
|
||
|
||
When making direct REST API calls with L1 authentication, include these headers:
|
||
|
||
| Header | Required? | Description |
|
||
| ---------------- | --------- | ---------------------- |
|
||
| `POLY_ADDRESS` | yes | Polygon signer address |
|
||
| `POLY_SIGNATURE` | yes | CLOB EIP 712 signature |
|
||
| `POLY_TIMESTAMP` | yes | Current UNIX timestamp |
|
||
| `POLY_NONCE` | yes | Nonce. Default 0 |
|
||
|
||
The `POLY_SIGNATURE` is generated by signing the following EIP-712 struct.
|
||
|
||
<Accordion title="EIP-712 Signing Example">
|
||
<CodeGroup>
|
||
```typescript Typescript theme={null}
|
||
const domain = {
|
||
name: "ClobAuthDomain",
|
||
version: "1",
|
||
chainId: chainId, // Polygon Chain ID 137
|
||
};
|
||
|
||
const types = {
|
||
ClobAuth: [
|
||
{ name: "address", type: "address" },
|
||
{ name: "timestamp", type: "string" },
|
||
{ name: "nonce", type: "uint256" },
|
||
{ name: "message", type: "string" },
|
||
],
|
||
};
|
||
|
||
const value = {
|
||
address: signingAddress, // The Signing address
|
||
timestamp: ts, // The CLOB API server timestamp
|
||
nonce: nonce, // The nonce used
|
||
message: "This message attests that I control the given wallet",
|
||
};
|
||
|
||
const sig = await signer._signTypedData(domain, types, value);
|
||
```
|
||
|
||
```python Python theme={null}
|
||
domain = {
|
||
"name": "ClobAuthDomain",
|
||
"version": "1",
|
||
"chainId": chainId, # Polygon Chain ID 137
|
||
}
|
||
|
||
types = {
|
||
"ClobAuth": [
|
||
{"name": "address", "type": "address"},
|
||
{"name": "timestamp", "type": "string"},
|
||
{"name": "nonce", "type": "uint256"},
|
||
{"name": "message", "type": "string"},
|
||
]
|
||
}
|
||
|
||
value = {
|
||
"address": signingAddress, # The signing address
|
||
"timestamp": ts, # The CLOB API server timestamp
|
||
"nonce": nonce, # The nonce used
|
||
"message": "This message attests that I control the given wallet",
|
||
}
|
||
|
||
sig = await signer._signTypedData(domain, types, value)
|
||
```
|
||
</CodeGroup>
|
||
</Accordion>
|
||
|
||
Reference implementations:
|
||
|
||
* [TypeScript](https://github.com/Polymarket/clob-client/blob/main/src/signing/eip712.ts)
|
||
* [Python](https://github.com/Polymarket/py-clob-client/blob/main/py_clob_client/signing/eip712.py)
|
||
|
||
***
|
||
|
||
**Create API Credentials**
|
||
|
||
Create new API credentials for user.
|
||
|
||
```bash theme={null}
|
||
POST {clob-endpoint}/auth/api-key
|
||
```
|
||
|
||
**Derive API Credentials**
|
||
|
||
Derive API credentials for user.
|
||
|
||
```bash theme={null}
|
||
GET {clob-endpoint}/auth/derive-api-key
|
||
```
|
||
|
||
**Response**
|
||
|
||
```json theme={null}
|
||
{
|
||
"apiKey": "550e8400-e29b-41d4-a716-446655440000",
|
||
"secret": "base64EncodedSecretString",
|
||
"passphrase": "randomPassphraseString"
|
||
}
|
||
```
|
||
|
||
**You'll need all three values for L2 authentication.**
|
||
|
||
***
|
||
|
||
## L2 Authentication
|
||
|
||
### What is L2?
|
||
|
||
The next level of authentication is called L2, and it consists of the
|
||
user's API credentials (apiKey, secret, passphrase) generated from L1
|
||
authentication. These are used solely to authenticate requests made to
|
||
the CLOB API. Requests are signed using HMAC-SHA256.
|
||
|
||
### What This Enables
|
||
|
||
Access to L2 methods such as posting signed/created orders, viewing open
|
||
orders, cancelling open orders, getting trades
|
||
|
||
* Cancel or get user's open orders
|
||
* Check user's balances and allowances
|
||
* Post user's signed orders
|
||
|
||
### CLOB Client
|
||
|
||
<Tabs>
|
||
<Tab title="TypeScript">
|
||
```typescript theme={null}
|
||
import { ClobClient } from "@polymarket/clob-client";
|
||
import { Wallet } from "ethers"; // v5.8.0
|
||
|
||
const HOST = "https://clob.polymarket.com";
|
||
const CHAIN_ID = 137; // Polygon mainnet
|
||
const signer = new Wallet(process.env.PRIVATE_KEY);
|
||
|
||
const client = new ClobClient(
|
||
HOST,
|
||
CHAIN_ID,
|
||
signer,
|
||
apiCreds, // Generated from L1 auth, API credentials enable L2 methods
|
||
1, // signatureType explained below
|
||
FUNDER // funder explained below
|
||
);
|
||
|
||
// Now you can trade!*
|
||
const order = await client.createAndPostOrder(
|
||
{ tokenID: "123456", price: 0.65, size: 100, side: "BUY" },
|
||
{ tickSize: "0.01", negRisk: false }
|
||
);
|
||
```
|
||
</Tab>
|
||
|
||
<Tab title="Python">
|
||
```python theme={null}
|
||
from py_clob_client.client import ClobClient
|
||
import os
|
||
|
||
host = "https://clob.polymarket.com"
|
||
chain_id = 137 # Polygon mainnet
|
||
private_key = os.getenv("PRIVATE_KEY")
|
||
|
||
client = ClobClient(
|
||
host="https://clob.polymarket.com",
|
||
chain_id=137,
|
||
key=os.getenv("PRIVATE_KEY"),
|
||
creds=api_creds, # Generated from L1 auth, API credentials enable L2 methods
|
||
signature_type=1, # signatureType explained below
|
||
funder=os.getenv("FUNDER_ADDRESS") # funder explained below
|
||
)
|
||
|
||
# Now you can trade!*
|
||
order = await client.create_and_post_order(
|
||
{"token_id": "123456", "price": 0.65, "size": 100, "side": "BUY"},
|
||
{"tick_size": "0.01", "neg_risk": False}
|
||
)
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
|
||
<Info>
|
||
Even with L2 authentication headers, methods that create user orders still require the user to sign the order payload.
|
||
</Info>
|
||
|
||
***
|
||
|
||
### REST API
|
||
|
||
While we highly recommend using our provided clients to handle signing
|
||
and authentication, the following is for developers who choose NOT to
|
||
use our [Python](https://github.com/Polymarket/py-clob-client) or
|
||
[TypeScript](https://github.com/Polymarket/clob-client) clients.
|
||
|
||
When making direct REST API calls with L2 authentication, include these headers:
|
||
|
||
| Header | Required? | Description |
|
||
| ----------------- | --------- | ----------------------------- |
|
||
| `POLY_ADDRESS` | yes | Polygon signer address |
|
||
| `POLY_SIGNATURE` | yes | HMAC signature for request |
|
||
| `POLY_TIMESTAMP` | yes | Current UNIX timestamp |
|
||
| `POLY_API_KEY` | yes | User's API `apiKey` value |
|
||
| `POLY_PASSPHRASE` | yes | User's API `passphrase` value |
|
||
|
||
The `POLY_SIGNATURE` for L2 is an HMAC-SHA256 signature created using the user's API credentials `secret` value.
|
||
Reference implementations can be found in the [Typescript](https://github.com/Polymarket/clob-client/blob/main/src/signing/hmac.ts)
|
||
and [Python](https://github.com/Polymarket/py-clob-client/blob/main/py_clob_client/signing/hmac.py) clients.
|
||
|
||
***
|
||
|
||
## Signature Types and Funder
|
||
|
||
When initializing the L2 client, you must specify your wallet **signatureType** and the **funder** address which holds the funds:
|
||
|
||
| Signature Type | Value | Description |
|
||
| -------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||
| EOA | 0 | Standard Ethereum wallet (MetaMask). Funder is the EOA address and will need POL to pay gas on transactions. |
|
||
| POLY\_PROXY | 1 | A custom proxy wallet only used with users who logged in via Magic Link email/Google. Using this requires the user to have exported their PK from Polymarket.com and imported into your app. |
|
||
| GNOSIS\_SAFE | 2 | Gnosis Safe multisig proxy wallet (most common). Use this for any new or returning user who does not fit the other 2 types. |
|
||
|
||
<Tip>
|
||
The wallet addresses displayed to the user on Polymarket.com is the proxy wallet and should be used as the funder.
|
||
These can be deterministically derived or you can deploy them on behalf of the user.
|
||
These proxy wallets are automatically deployed for the user on their first login to Polymarket.com.
|
||
</Tip>
|
||
|
||
***
|
||
|
||
## Troubleshooting
|
||
|
||
<AccordionGroup>
|
||
<Accordion title="Error: INVALID_SIGNATURE">
|
||
Your wallet's private key is incorrect or improperly formatted.
|
||
|
||
**Solution:**
|
||
|
||
* Verify your private key is a valid hex string (starts with "0x")
|
||
* Ensure you're using the correct key for the intended address
|
||
* Check that the key has proper permissions
|
||
</Accordion>
|
||
|
||
<Accordion title="Error: NONCE_ALREADY_USED">
|
||
The nonce you provided has already been used to create an API key.
|
||
|
||
**Solution:**
|
||
|
||
* Use `deriveApiKey()` with the same nonce to retrieve existing credentials
|
||
* Or use a different nonce with `createApiKey()`
|
||
</Accordion>
|
||
|
||
<Accordion title="Error: Invalid Funder Address">
|
||
Your funder address is incorrect or doesn't match your wallet.
|
||
|
||
**Solution:** Check your Polymarket profile address at [polymarket.com/settings](https://polymarket.com/settings).
|
||
|
||
If it does not exist or user has never logged into Polymarket.com, deploy it first before creating L2 authentication.
|
||
</Accordion>
|
||
|
||
<Accordion title="Lost API credentials but have nonce">
|
||
```typescript theme={null}
|
||
// Use deriveApiKey with the original nonce
|
||
const recovered = await client.deriveApiKey(originalNonce);
|
||
```
|
||
</Accordion>
|
||
|
||
<Accordion title="Lost both credentials and nonce">
|
||
Unfortunately, there's no way to recover lost API credentials without the nonce. You'll need to create new credentials:
|
||
|
||
```typescript theme={null}
|
||
// Create fresh credentials with a new nonce
|
||
const newCreds = await client.createApiKey();
|
||
// Save the nonce this time!
|
||
```
|
||
</Accordion>
|
||
</AccordionGroup>
|
||
|
||
***
|
||
|
||
## See Client Methods
|
||
|
||
<CardGroup cols={2}>
|
||
<Card title="Public Methods" icon="globe" href="/developers/CLOB/clients/methods-public">
|
||
Access market data, orderbooks, and prices.
|
||
</Card>
|
||
|
||
<Card title="L1 Methods" icon="key" href="/developers/CLOB/clients/methods-l1">
|
||
Private key authentication to create or derive API keys (L2 headers).
|
||
</Card>
|
||
|
||
<Card title="L2 Methods" icon="lock" href="/developers/CLOB/clients/methods-l2">
|
||
Manage and close orders. Creating orders requires signer.
|
||
</Card>
|
||
|
||
<Card title="Builder Program Methods" icon="hammer" href="/developers/CLOB/clients/methods-builder">
|
||
Builder-specific operations for those in the Builders Program.
|
||
</Card>
|
||
</CardGroup>
|