ASK KNOX
beta
LESSON 282

Proxy / Funder Footguns

Polymarket's signature_type matrix. When your EOA is the funder, when it is not, and how to detect the misconfiguration before it costs you. The balance check that lied because it polled the wrong address — extended to proxy wallets.

7 min read

Polymarket supports three signature types. For type 0, the signer EOA is also the funder — the address that holds USDC.e is the same address that signs orders. For types 1 and 2, the signer and funder are different: the signer is an EOA controlled by the user, and the funder is a proxy smart contract that actually holds the collateral.

The proxy/funder footgun is what happens when a bot built for one signature type is configured against the assumptions of another — or when the bot queries the wrong side of the signer/funder split and ends up reporting "balance = 0" on a fully-funded account.

The Two Variants

Variant A — signature_type=0, queries expected wallet (correct):

The signer derives from the private key. The funder is the same address. usdc_e.balanceOf(derived) returns the correct balance. This is the clean case — and the case Hermes was originally designed for.

Variant B — signature_type=1/2 bot, queries signer EOA (wrong):

The signer derives from a private key held by the user. The funder is a proxy wallet controlled by that signer via a smart contract relationship. usdc_e.balanceOf(derived) returns zero because the signer EOA holds nothing — the proxy does. The bot reports "unfunded" while the proxy is full.

The footgun is that the second variant can look exactly like the first variant from the bot's perspective. If the bot was ported from a type-0 codebase to a type-1 codebase without updating the balance-query logic, it will silently poll the wrong address.

Inline Diagram — The Four-Address Problem

PROXY / FUNDER ADDRESS MATRIXTYPE 0 — SIGNER = FUNDERsigner = funder = 0xCF56…0aDbalanceOf(derived) → correctone address, no gapTYPE 1 / 2 — SIGNER ≠ FUNDERsigner 0xABC…proxy 0xDEF…emptyfundedbalanceOf(signer) → zero (wrong address)

The Detection Pattern

At startup, any bot using signature_type 1 or 2 should log BOTH balances:

signer = Account.from_key(private_key).address
funder = clob.get_funder_address(signer)  # proxy lookup via exchange API

signer_balance = usdc_e.balanceOf(signer)
funder_balance = usdc_e.balanceOf(funder)
clob_reported = clob.get_balance_allowance(asset=USDC_E, wallet=funder)["balance"]

logger.info(f"wallets signer={signer} signer_bal={signer_balance} "
            f"funder={funder} funder_bal={funder_balance} clob={clob_reported}")

# The canonical balance is the funder balance. Alert if it diverges from clob_reported.
assert funder_balance == clob_reported, "on-chain funder balance != exchange report"

This makes the gap visible in a single log line. If the signer is empty and the funder is funded, the bot's worldview is consistent and trading can proceed. If the signer is funded and the funder is empty, something is deeply wrong — probably a signature type misconfiguration.

The Rule

Types 0, 1, and 2 each have a different signer/funder relationship. The bot must know which one it uses and query the correct side for balance. Type 0 is simple. Types 1 and 2 require explicit proxy lookup and two-address logging. The footgun is skipping the proxy step — and the cost is a false "unfunded" reading that can burn hours of debugging.