go-ethereum in the Polymarket signing code (P2-10 notes)
Written 2026-09-30 for Phase 3, as input to ADR 0010 (the tech lead writes the ADR). Security finding P2-10 asked for either a small EIP-712 encoder or a recorded acceptance of go-ethereum. Phase 3 keeps go-ethereum (v1.17.6, backend/go.mod); these notes say what is imported, what
it pulls in, and what it is trusted for.
What the code imports
Onlybackend/internal/venue/polymarket/signing imports go-ethereum for signing. The adapter
package (venue/polymarket) uses common (the Address type) and common/hexutil (hex encoding
of the order signature).
Code written here, not taken from go-ethereum: the typed-data allowlist (
signing/typeddata.go),
the ERC-7739 signature layout and the session-key wrapper (signing/order.go), and the amount
maths (signing/amounts.go).
What it pulls in
go list -deps of the signing package with CGO_ENABLED=0 lists 66 non-standard-library
packages. Most come from apitypes, which imports core/types (transactions, blocks), params,
rlp, event, log, crypto/kzg4844, and through them gnark-crypto (BLS12-381),
go-eth-kzg, holiman/uint256 and bits-and-blooms/bitset. None of that is called by the signing
code; it is compiled in because apitypes declares transaction types beside the typed-data code.
crypto alone needs far less: common, common/hexutil, common/math, crypto/keccak, rlp,
holiman/uint256, golang.org/x/sys/cpu and the secp256k1 implementation.
secp256k1 backend. With CGO_ENABLED=0, crypto signs with the pure-Go
github.com/decred/dcrd/dcrec/secp256k1/v4. With cgo on (for example a default Linux build), it
compiles go-ethereum’s crypto/secp256k1, a C copy of libsecp256k1. Both give the same RFC 6979
deterministic signatures, which is why the golden vectors match on Windows (cgo off) and must
match on Linux. PLAN Phase 6 builds with CGO_ENABLED=0 everywhere (P2-10 disposition), which
keeps one backend.
Why keeping it is acceptable
- Checked output. Every hash and signature the package produces is compared byte for byte
with vectors from the official TS SDK: 2 ClobAuth, 9 orders (both exchanges, signature types 0
and 3, BUY and SELL, GTD with non-zero metadata and builder), 26 amount cases, 10 HMAC cases.
The SDK’s own
TypedDataSignpayloads, hashed byapitypes, give the vectors’ digests too. - Narrow use. The package calls three things:
apitypeshashing,crypto.Sign, and address helpers. It never uses go-ethereum’s keystore, RPC, transaction building or account managers. - One signing path. Every signature goes through
signTypedData, which first requires the primary type, the full type set and the domain to be one of ClobAuth or a CLOB V2 order on the two pinned exchanges on chain 137 (TestSignTypedDataRefusesOthers, gate item 13). A bug or misuse elsewhere in go-ethereum cannot widen what the key signs. - Scanned.
govulncheck ./internal/venue/...reports no vulnerabilities (2026-09-30).
Costs and what would change the decision
- Binary size and build time grow with the unused
core/types, KZG and BLS code. - A go-ethereum upgrade can move
apitypesbehaviour (it did change encoding rules across versions). The golden vectors catch a change in any hash we produce; upgrades should be deliberate and rungo test ./internal/venue/.... - Replacing
apitypeswith a small fixed encoder for the three structs (about 100 lines overcrypto/keccak) would drop most of the transitive tree.cryptowould stay for secp256k1. The vectors already pin every value such an encoder must produce.