You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Web3.py对接Polymarket CLOB API时EIP-712签名报错:无效域键ClobAuth

问题描述

使用Web3.py与Polymarket的CLOB API交互,尝试签署EIP-712结构化消息创建API密钥时,触发错误:

[ERROR] Failed to sign typed data: Invalid domain key: ClobAuth

已尝试操作

  • 确认ClobAuth域和EIP-712签名类型配置正确
  • 检查Web3.py已通过Alchemy正确连接Polygon主网

环境信息

  • Python版本:3.12
  • Web3.py版本:5.x.x
  • 使用py_clob_client与Polymarket API交互

相关代码

import requests
from web3 import Web3
from dotenv import load_dotenv
import os
import time  # 原代码遗漏该模块导入

# Load the .env file to access private keys and other environment variables
load_dotenv()


# Function to implement manual PoA middleware injection
def geth_poa_middleware(make_request, web3):
    def middleware(method, params):
        if method == "eth_getBlockByNumber":
            params = list(params)
            if params[0] == "pending":
                params[0] = "latest"
        return make_request(method, params)

    return middleware


from py_clob_client.client import ClobClient
from py_clob_client.clob_types import OrderArgs, OrderType, ApiCreds
from py_clob_client.order_builder.constants import BUY, SELL

# --- Configuration ---
CLOB_ENDPOINT = "https://clob.polymarket.com/"  # API Endpoint
CHAIN_ID = 137  # Polygon Mainnet. Use 80001 for testnet (Mumbai)


# --- Authentication ---

def create_api_key(private_key: str) -> ApiCreds:
    """Creates API key using private key authentication (L1)."""

    print("[DEBUG] Initializing Web3 with Polygon mainnet.")
    w3 = Web3(Web3.HTTPProvider(f"https://polygon-mainnet.g.alchemy.com/v2/{os.getenv('YOUR_ALCHEMY_KEY')}"))

    # Inject the PoA middleware manually
    w3.middleware_onion.inject(geth_poa_middleware, layer=0)

    # Create account from private key
    account = w3.eth.account.from_key(private_key)
    address = account.address
    print(f"[DEBUG] Created account with address: {address}")

    ts = str(int(time.time()))
    nonce = 0  # Set to 0 initially
    message = "This message attests that I control the given wallet"

    print("[DEBUG] Constructing domain and types for EIP-712 signature.")
    # Define domain and types according to Polymarket specs
    domain = {
        "name": "ClobAuthDomain",
        "version": "1",
        "chainId": CHAIN_ID,
    }

    # The types for the signature, as per EIP-712
    types = {
        "ClobAuth": [
            {"name": "address", "type": "address"},
            {"name": "timestamp", "type": "string"},
            {"name": "nonce", "type": "uint256"},
            {"name": "message", "type": "string"},
        ],
    }

    # Values for the signature
    value = {
        "address": address,
        "timestamp": ts,
        "nonce": nonce,
        "message": message,
    }

    print("[DEBUG] Value for signing:")
    print(value)

    try:
        # Encode the structured data for EIP-712
        signed_message = w3.eth.account.sign_typed_data(domain, types, value)
        signature = signed_message.signature.hex()
        print(f"[DEBUG] Signature: {signature}")
    except Exception as e:
        print(f"[ERROR] Failed to sign typed data: {e}")
        return None

    # Prepare headers with the signature
    headers = {
        'POLY_ADDRESS': address,
        'POLY_SIGNATURE': signature,
        'POLY_TIMESTAMP': ts,
        'POLY_NONCE': str(nonce)
    }

    print(f"[DEBUG] Sending POST request to {CLOB_ENDPOINT}auth/api-key")
    response = requests.post(f"{CLOB_ENDPOINT}auth/api-key", headers=headers)

    # Print the response status
    print(f"[DEBUG] Response Status Code: {response.status_code}")

    try:
        response.raise_for_status()  # Raise an error for bad responses
        creds = ApiCreds(**response.json())
        print(f"[DEBUG] API Key created: {creds.apiKey}")
        return creds
    except requests.exceptions.HTTPError as e:
        print(f"[ERROR] HTTPError: {e.response.text}")
    except Exception as e:
        print(f"[ERROR] Unexpected error: {e}")

    return None


# --- Trading ---

def execute_trade(client: ClobClient, token_id: str, side: str, size: float, price: float,
                  order_type: OrderType = OrderType.GTC) -> dict:
    """Executes a trade on Polymarket."""

    print(f"[DEBUG] Preparing order: Side={side}, Size={size}, Price={price}, Token ID={token_id}")

    order_args = OrderArgs(
        price=price,
        size=size,
        side=BUY if side.upper() == "BUY" else SELL,  # Ensure side is correctly formatted
        token_id=token_id
    )

    print("[DEBUG] Signing the order.")
    try:
        signed_order = client.create_order(order_args)
        print(f"[DEBUG] Signed Order: {signed_order}")
    except Exception as e:
        print(f"[ERROR] Error signing order: {e}")
        return {}

    print("[DEBUG] Posting the order to the Polymarket CLOB.")
    try:
        response = client.post_order(signed_order, order_type)
        print(f"[DEBUG] Order Response: {response}")
        return response
    except Exception as e:
        print(f"[ERROR] Error posting order: {e}")
        return {}


# Example usage (replace with your actual values):

if __name__ == "__main__":
    private_key = os.getenv('private_key')  # Your Polygon private key, ensure it is loaded correctly

    # Check if private_key is loaded
    if not private_key:
        print("[ERROR] Private key not loaded from .env file.")
        exit(1)

    # 1. Create/Get API Keys
    try:
        creds = create_api_key(private_key)
        if not creds:
            print("[ERROR] Failed to create API Key.")
            exit(1)

        print(f"[INFO] Created API Key: {creds.apiKey}")
        print(f"[INFO] Secret: {creds.secret}")  # Be careful not to expose this!
        print(f"[INFO] Passphrase: {creds.passphrase}")  # Be careful not to expose this!

    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 409:  # Key already exists
            print("[INFO] API Key already exists, deriving it.")
            client_l1 = ClobClient(CLOB_ENDPOINT, key=private_key, chain_id=CHAIN_ID)
            creds = client_l1.derive_api_key()  # Retrieve existing key
            print(f"[INFO] Derived existing API Key: {creds.apiKey}")
        else:
            print(f"[ERROR] HTTP Error during API key creation: {e}")
            exit(1)

    # 2. Initialize Trading Client (using API Key authentication now)
    print("[DEBUG] Initializing the ClobClient with the API credentials.")
    client = ClobClient(CLOB_ENDPOINT, key=creds.apiKey, secret=creds.secret, passphrase=creds.passphrase,
                        chain_id=CHAIN_ID)

    # 3. Example Trade
    token_id = os.getenv('YOUR_TOKEN_ID')  # Replace with the actual token ID

    # Check if token_id is loaded
    if not token_id:
        print("[ERROR] Token ID not loaded from environment variables.")
        exit(1)

    side = "BUY"
    size = 10.0
    price = 0.6  # Example price. Get this from market data.
    order_type = OrderType.GTC  # Or OrderType.FOK or OrderType.GTD

    try:
        print("[INFO] Executing trade.")
        trade_response = execute_trade(client, token_id, side, size, price, order_type)
        print(f"[INFO] Trade Response: {trade_response}")
    except Exception as e:
        print(f"[ERROR] Error executing trade: {e}")
解决方案

错误的核心原因是Web3.py v5的sign_typed_data方法要求在types字典中必须显式定义EIP712Domain类型,代码中仅定义了ClobAuth类型,导致签名时无法正确识别域结构。

修改步骤

  1. 补充EIP712Domain类型定义
    在types字典中添加与domain字段对应的EIP712Domain类型描述:
types = {
    "EIP712Domain": [
        {"name": "name", "type": "string"},
        {"name": "version", "type": "string"},
        {"name": "chainId", "type": "uint256"},
    ],
    "ClobAuth": [
        {"name": "address", "type": "address"},
        {"name": "timestamp", "type": "string"},
        {"name": "nonce", "type": "uint256"},
        {"name": "message", "type": "string"},
    ],
}
  1. 修复模块导入问题
    在代码顶部添加time模块导入,避免time.time()调用报错:
import time
  1. 可选:使用py_clob_client内置方法简化流程
    可以直接用py_clob_client提供的方法生成API密钥,无需手动构建EIP-712结构:
client_l1 = ClobClient(CLOB_ENDPOINT, key=private_key, chain_id=CHAIN_ID)
creds = client_l1.create_api_key()

内容的提问来源于stack exchange,提问作者ahron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:05:52