Skip to content

# Feature Request: Add Proxy Wallet Support (signature_type and funder) to py-builder-relayer-client #3

Description

@pashadude

Feature Request: Add Proxy Wallet Support (signature_type and funder) to py-builder-relayer-client

Summary

The Python py-builder-relayer-client does not support proxy wallet functionality (signature_type=2) that allows builders to execute transactions on behalf of users without requiring the user's private key. This feature exists in both the TypeScript builder-relayer-client and the Python py-clob-client, but is missing from the Python relayer client.

Current Behavior

The RelayClient.__init__() only accepts:

  • relayer_url
  • chain_id
  • private_key
  • builder_config

Current Python implementation:

# py_builder_relayer_client/client.py
class RelayClient:
    def __init__(
        self,
        relayer_url,
        chain_id: int,
        private_key: str = None,
        builder_config: BuilderConfig = None,
    ):
        # ...
        self.signer = Signer(private_key, chain_id) if private_key else None

This means:

  • The Safe wallet address is always auto-derived from the private_key using CREATE2
  • There's no way to specify a custom Safe address or use a funder/proxy pattern
  • Users MUST share their private key with the builder to trade

Expected Behavior

The Python relayer client should support the same proxy wallet pattern as:

  1. TypeScript builder-relayer-client (as shown in the docs)
  2. Python py-clob-client (already implemented)

TypeScript Implementation (Reference)

From the Builder Signing Server docs:

import { RelayClient } from "@polymarket/builder-relayer-client";
import { BuilderConfig } from "@polymarket/builder-signing-sdk";

const builderConfig = new BuilderConfig({
  remoteBuilderConfig: {url: "http://localhost:3000/sign"}
});

const relayClient = new RelayClient(
  relayerUrl,
  chainId,
  wallet,  // Builder's wallet (proxy signer)
  builderConfig
);

Python py-clob-client Implementation (Already Works)

# py-clob-client/py_clob_client/client.py (lines 108-138)
class ClobClient:
    def __init__(
        self,
        host,
        chain_id: int = None,
        key: str = None,
        creds: ApiCreds = None,
        signature_type: int = None,  # ✅ SUPPORTED
        funder: str = None,           # ✅ SUPPORTED
        builder_config: BuilderConfig = None,
    ):
        # ...
        if self.signer:
            self.builder = OrderBuilder(
                self.signer, sig_type=signature_type, funder=funder
            )

Proposed Solution

Add signature_type and funder parameters to RelayClient:

# Proposed change to py_builder_relayer_client/client.py
class RelayClient:
    def __init__(
        self,
        relayer_url,
        chain_id: int,
        private_key: str = None,
        builder_config: BuilderConfig = None,
        signature_type: int = 0,      # NEW: 0=EOA, 1=Email/Magic, 2=Proxy
        funder: str = None,            # NEW: Client's EOA for proxy wallets
    ):
        self.relayer_url = (
            relayer_url[0:-1] if relayer_url.endswith("/") else relayer_url
        )
        self.chain_id = chain_id
        self.contract_config = get_contract_config(chain_id)
        
        self.signature_type = signature_type
        self.funder = funder

        self.signer = None
        if private_key is not None:
            self.signer = Signer(private_key, chain_id)
        
        self.builder_config = None
        if builder_config is not None:
            self.builder_config = builder_config
        self.logger = logging.getLogger(self.__class__.__name__)

Additionally, the derive() function and execute() method would need to be updated to use the funder address instead of the signer's address when signature_type=2.

Use Case

Scenario: A builder (trading service) wants to execute trades for their users without collecting private keys.

With current implementation: ❌ Impossible - must use auto-derived Safe

With proxy wallet support:

# Builder's private key (not the user's!)
service_pk = os.getenv("SERVICE_PK")
# User's EOA address (owns their Safe wallet)
user_eoa = "0x8E9c68Ae342CF4cC5a888BC99686590014DA3719"
# User's existing Safe wallet
user_safe = "0xdF15830e070437f4593e1F7d5Fe32F2ec388319C"

builder_config = BuilderConfig(
    local_builder_creds=BuilderApiKeyCreds(
        key=os.getenv("BUILDER_API_KEY"),
        secret=os.getenv("BUILDER_SECRET"),
        passphrase=os.getenv("BUILDER_PASS_PHRASE"),
    )
)

client = RelayClient(
    relayer_url="https://relayer-v2.polymarket.com/",
    chain_id=137,
    private_key=service_pk,      # Builder's PK
    builder_config=builder_config,
    signature_type=2,             # Proxy wallet
    funder=user_eoa,              # User's EOA (doesn't share PK)
)

# Execute trade on user's Safe with Builder attribution
resp = client.execute([approve_txn, split_txn], "10 USDC BTC Trade")

Additional Context

  • This feature is critical for builders who want to provide custodial trading services
  • The py-clob-client already implements this correctly for orderbook trading
  • The TypeScript relayer client supports this pattern
  • Without this, builders must either:
    1. Collect user private keys (security risk)
    2. Have users transfer funds to builder-controlled Safes (poor UX)
    3. Only use orderbook trading via CLOB client (limited functionality)

Files to Modify

  1. py_builder_relayer_client/client.py - Add parameters to __init__ and execute()
  2. py_builder_relayer_client/builder/safe.py - Update to use funder for Safe derivation
  3. py_builder_relayer_client/signer.py - May need updates for proxy signature handling

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions