What it is
User-owned execution is an optional trading mode for applications whose users should trade as themselves instead of attributing activity to a builder account.Requires
polynode-sdk >= 0.13.0, Python polynode >= 0.13.0, or Rust
polynode >= 0.16.0 with the trading feature.executionMode: "user_owned" in TypeScript,
execution_mode=ExecutionMode.USER_OWNED in Python, or
execution_mode: ExecutionMode::UserOwned in Rust. Builder mode remains the
default, so existing integrations do not change unless they opt in.
In user-owned mode:
- Orders use the user’s normal CLOB credentials and carry zero builder attribution.
- Gasless smart-wallet operations use authorization owned by the signing wallet, not a builder credential.
- Builder credentials, builder authentication headers, and nonzero builder overrides are rejected.
- Activity does not consume a builder’s relayer allowance.
Why use it
Use user-owned execution when a user should be able to trade independently of your platform’s builder allowance and your platform does not need builder credit for that activity. Keep the default builder mode when your platform wants builder attribution or uses its own builder credentials.Wallet control and custody
Polynode does not receive or store the user’s private key, seed phrase, or wallet signer. The user’s wallet signs the ownership message, orders, and smart-wallet actions. Your application should never ask a user to send you a private key. The returned wallet-owned relayer credential is not a wallet key and cannot sign an order, approve a token, or transfer funds by itself. Treat it as a backend secret because it is tied to that user’s gasless execution account. The SDK keeps the credential in memory unless your application explicitly saves it. Store it only in your own backend secret manager. Do not put it in browser storage, logs, analytics, source control, or client-visible configuration.Platform integration
Treat one signing wallet as the unit of isolation. A platform serving many users should keep a separate wallet-owned relayer credential for each wallet address and create a separate trader context for the active wallet. Never share one trader instance or relayer credential across two users. For each wallet:- Load that wallet’s credential from your encrypted backend secret store.
- If it does not exist, run the two-step browser authorization below and save the returned credential under the same expected wallet address.
- Pass the credential to that wallet’s trader configuration and complete onboarding or trading.
- Clear the trader context when the request, worker job, or user session ends.
Supported wallets
The first release supports normal EOA signers and EOA-controlled Safe or deposit wallet accounts. The signer must support a standard personal-message signature. Gaslesssplit and merge require an EOA-controlled Safe or deposit wallet. A
plain EOA can sign orders, but these gasless position methods fail closed rather
than silently routing through a different wallet.
Legacy POLY_PROXY accounts and Magic/DID signers are not supported in
user-owned mode. Keep those accounts on the default builder path for now.
Browser-wallet authorization
Use the two-step API when the user signs in a browser. Keep the Polynode API key and the complete challenge object on your backend; send only the displayed message to the user’s wallet.Backend
Browser
begin_user_relayer_authorization() and
complete_user_relayer_authorization() in Python, and
PolyNodeTrader::begin_user_relayer_authorization() and
PolyNodeTrader::complete_user_relayer_authorization() in Rust.
TypeScript
For a caller-controlled service wallet or signing callback, the combined flow is one call:await trader.authorizeUserOwnedExecution(userControlledSigner) when you
want the combined authorization result without running the rest of onboarding.
Python
await trader.authorize_user_owned_execution(user_controlled_signer) when
you want the combined authorization result without running full onboarding.
The existing synchronous trader.split() and trader.merge() methods remain
build-only helpers; use the explicit execute_* methods above for gasless
submission.
Rust
authorize_user_owned_execution(&signer) when you want the combined result
without running full onboarding. Supply a previously saved credential through
TraderConfig.user_relayer_credentials on later runs.
CLOB transport
User-owned orders go directly from the SDK to the official Polymarket CLOB by default. There is no automatic fallback between transports, which prevents an ambiguous response from creating a duplicate order. Integrations may explicitly select Polynode’s regional proxy:
The proxy changes only the network path. It does not add builder attribution or
take wallet custody.
Important limitations
- User-owned mode does not support Polynode fee escrow. A positive SDK fee configuration fails before signing or submitting an order.
- Normal CLOB credentials are still required and should be protected as secrets.
- Market balances, token approvals, minimum sizes, rate limits, and order rules are unchanged.
- A split must be followed by a merge of the same complete set to restore the original collateral amount.
Integration checklist
- Enable user-owned execution only for users who opt in.
- Keep the Polynode API key and full challenge object on your backend.
- Ask the wallet to sign only the exact message returned by the SDK.
- Bind the returned signature and credential to the same expected address.
- Keep wallet-owned and CLOB credentials in your backend secret manager.
- Do not pass builder credentials, a nonzero builder code, or a positive fee configuration.
- Confirm the ready status reports user-owned execution before submitting.
- Close the trader on shutdown to clear its active signer and in-memory credential.

