auth

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package auth provides Polymarket authentication primitives — L0 / L1 / L2 auth, EIP-712 signing, deposit-wallet CREATE2 derivation, and builder attribution.

Used by every signed request to the CLOB and relayer. The default mode for polygolem is read-only and never enters this package; mutating commands acquire signers here behind explicit gates. Start with the Signer types and DeriveDepositWallet for orientation.

This package is internal and not part of the polygolem public SDK.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertL1

func AssertL1(level AccessLevel) error

AssertL1 returns an error if the access level is below L1.

func AssertL2

func AssertL2(level AccessLevel) error

AssertL2 returns an error if the access level is below L2.

func BuildBuilderHeaders

func BuildBuilderHeaders(bc *BuilderConfig, timestamp int64, method, path string, body *string) (map[string]string, error)

BuildBuilderHeaders builds builder attribution headers. Separate from L2 user credentials per PRD R3.

func BuildClobAuthTypedData

func BuildClobAuthTypedData(address string, chainID uint64, timestamp string, nonce uint64) (EIP712Domain, ClobAuthMessage)

BuildClobAuthTypedData builds the EIP-712 typed data for Polymarket ClobAuth. Used for L1 API key creation/derivation.

func BuildL1HeadersForAddress added in v0.1.1

func BuildL1HeadersForAddress(privateKeyHex string, chainID int64, timestamp int64, nonce int64, ownerAddress string) (map[string]string, error)

BuildL1HeadersForAddress builds raw EOA-signed ClobAuth headers. When ownerAddress is non-empty it overrides both POLY_ADDRESS and the typed-data value.address, but the signature remains a raw EOA signature.

Do not use this helper for deposit-wallet-owned API-key minting; use BuildL1HeadersForDepositWallet so POLY_SIGNATURE is the ERC-7739 wrapped form validated through the deployed wallet's ERC-1271 hook.

func BuildL1HeadersForDepositWallet added in v0.1.1

func BuildL1HeadersForDepositWallet(privateKeyHex string, chainID int64, timestamp int64, nonce int64, depositWalletAddress string) (map[string]string, error)

BuildL1HeadersForDepositWallet builds CLOB L1 auth headers wrapped with the ERC-7739 nested EIP-712 envelope for use against a Polymarket deposit wallet's `isValidSignature`. The EOA holding privateKey produces the inner ECDSA, but POLY_ADDRESS is the smart wallet and POLY_SIGNATURE is the wrapped form Solady's ERC1271 mixin expects.

Required when minting an L2 API key bound to a deposit wallet (so the L2 key's address matches `Order.signer == Order.maker == depositWallet` for sigtype 3 — both the CLOB HTTP gate and the on-chain `_verifyPoly1271Signature` use this equality).

The deposit wallet must be deployed at depositWalletAddress before this call (the contract's `isValidSignature` runs on-chain).

func BuildL1HeadersFromPrivateKey

func BuildL1HeadersFromPrivateKey(privateKeyHex string, chainID int64, timestamp int64, nonce int64) (map[string]string, error)

BuildL1HeadersFromPrivateKey builds Polymarket CLOB L1 auth headers for API-key creation and derivation. It signs the canonical ClobAuth EIP-712 message with a local EOA private key. POLY_ADDRESS is the EOA address.

func BuildL2Headers

func BuildL2Headers(apiKey *APIKey, timestamp int64, method, path string, body *string) (map[string]string, error)

BuildL2Headers builds the L2 HMAC authentication headers. Signature = base64url(HMAC-SHA256(decoded_secret, timestamp + method + path + body)) Body must be compact JSON (no spaces).

func BuildSIWEBearerToken

func BuildSIWEBearerToken(msg SIWEMessage, signature []byte) (string, error)

BuildSIWEBearerToken assembles the Polymarket /login bearer token: base64( JSON(message fields) + ":::" + 0x-prefixed signature hex ).

func CompactJSON

func CompactJSON(s string) string

CompactJSON removes all whitespace from a JSON string for HMAC body signing.

func EIP712ChainID

func EIP712ChainID(chainID int64) *gethmath.HexOrDecimal256

func GeneratePrivateKey

func GeneratePrivateKey() (string, error)

GeneratePrivateKey creates a new random secp256k1 key.

func HashTypedData

func HashTypedData(domain EIP712Domain, message ClobAuthMessage) ([32]byte, error)

HashTypedData computes the EIP-712 typed data hash. Implements the algorithm from EIP-712:

encode(domainSeparator, message) = 0x19 0x01 ‖ domainSeparator ‖ hashStruct(message)

where:

domainSeparator = hashStruct(eip712Domain)
hashStruct(s) = keccak256(typeHash ‖ encodeData(s))
typeHash = keccak256(typeName ‖ "(" ‖ memberTypes ‖ ")")

func L1HeaderMap

func L1HeaderMap(address string, signature [32]byte, timestamp string, nonce uint64) map[string]string

L1HeaderMap builds the L1 authentication headers for API key operations.

func MakerAddressForSignatureType

func MakerAddressForSignatureType(signerAddress string, chainID int64, signatureType int) (string, error)

MakerAddressForSignatureType returns the CLOB maker/funder address for the configured signature type. EOA orders use the signer directly; proxy and Safe modes use Polymarket's deterministic CREATE2 wallet addresses on Polygon.

func PrivateKeyToAddress

func PrivateKeyToAddress(privateKeyHex string) (string, error)

PrivateKeyToAddress derives the Ethereum address from a hex private key.

func Redact

func Redact(v string) string

Redact replaces a secret value with a safe representation.

func SignClobAuth

func SignClobAuth(signer Signer, chainID uint64, timestamp string, nonce uint64) ([32]byte, error)

SignClobAuth performs the full L1 signing flow: 1. Builds EIP-712 domain and message 2. Computes typed data hash 3. Signs with provided signer

func SignHMAC

func SignHMAC(secret string, timestamp int64, method, path string, body *string) string

SignHMAC computes a Polymarket-compatible HMAC signature.

func WrapERC7739Signature added in v0.1.1

func WrapERC7739Signature(signer *PrivateKeySigner, depositWalletAddress string, chainID int64, appDomainSep [32]byte, contents [32]byte, contentsType string) (string, error)

WrapERC7739Signature produces a Solady-compatible ERC-7739 nested EIP-712 wrapped signature suitable for a Polymarket deposit wallet's `isValidSignature` check. The deposit wallet's `_erc1271IsValidSignatureViaNestedEIP712` recomputes:

finalHash = keccak256(0x1901 || appDomainSep ||
            hashStruct(TypedDataSign{contents, DepositWallet inline domain}))

recovers the EOA from innerSig, and returns ERC1271_MAGIC if the recovered address matches the wallet's owner.

Layout: innerSig(65) || appDomainSep(32) || contents(32) || contentsType || uint16BE(len(contentsType)).

Caller responsibilities:

  • appDomainSep is the OUTER domain separator the dapp/contract uses (e.g. CTF Exchange V2 for orders, ClobAuthDomain for L1 mints).
  • contents is the EIP-712 hashStruct of the original typed-data.
  • contentsType is the canonical type string (must start with the primary type name, e.g. "Order(...)" or "ClobAuth(...)").
  • depositWalletAddress is the smart wallet that will validate the signature; it must be deployed.

Types

type APIKey

type APIKey struct {
	Key        string `json:"key"`
	Secret     string `json:"secret"`
	Passphrase string `json:"passphrase"`
}

APIKey holds L2 credentials for HMAC-authenticated requests.

func (*APIKey) Redacted

func (k *APIKey) Redacted() APIKey

func (*APIKey) Validate

func (k *APIKey) Validate() error

type AccessLevel

type AccessLevel int

AccessLevel defines the authentication tier.

const (
	L0 AccessLevel = iota // public, no credentials
	L1                    // wallet signer for key creation/derivation
	L2                    // API key + HMAC for authenticated operations
)

func (AccessLevel) String

func (l AccessLevel) String() string

type BuilderConfig

type BuilderConfig struct {
	Key        string
	Secret     string
	Passphrase string
}

BuilderConfig holds credentials for builder attribution headers. Builder credentials must be kept separate from user L2 credentials (per PRD R3).

func (*BuilderConfig) Valid

func (bc *BuilderConfig) Valid() bool

type ClobAuthMessage

type ClobAuthMessage struct {
	Address   string `json:"address"`
	Timestamp string `json:"timestamp"`
	Nonce     uint64 `json:"nonce"`
	Message   string `json:"message"`
}

ClobAuthMessage is the EIP-712 typed data payload for ClobAuth.

type EIP712Domain

type EIP712Domain struct {
	Name              string `json:"name"`
	Version           string `json:"version"`
	ChainID           uint64 `json:"chainId"`
	VerifyingContract string `json:"verifyingContract,omitempty"`
	Salt              string `json:"salt,omitempty"`
}

EIP712Domain represents the EIP-712 domain separator.

type PrivateKeySigner

type PrivateKeySigner struct {
	// contains filtered or unexported fields
}

PrivateKeySigner implements Signer using go-ethereum/secp256k1.

func NewPrivateKeySigner

func NewPrivateKeySigner(privateKeyHex string, chainID int64) (*PrivateKeySigner, error)

NewPrivateKeySigner creates a signer from a 0x-prefixed hex private key.

func (*PrivateKeySigner) Address

func (s *PrivateKeySigner) Address() string

func (*PrivateKeySigner) ChainID

func (s *PrivateKeySigner) ChainID() int64

func (*PrivateKeySigner) SignEIP712

func (s *PrivateKeySigner) SignEIP712(typed apitypes.TypedData) ([]byte, error)

SignEIP712 signs canonical EIP-712 typed data and returns the full 65-byte Ethereum signature with a 27/28 recovery byte.

func (*PrivateKeySigner) SignHash

func (s *PrivateKeySigner) SignHash(hash [32]byte) ([]byte, error)

SignHash signs a 32-byte hash using personal_sign prefix.

func (*PrivateKeySigner) SignPersonalMessage

func (s *PrivateKeySigner) SignPersonalMessage(msg []byte) ([]byte, error)

SignPersonalMessage signs an arbitrary-length byte slice using the EIP-191 personal_sign scheme: keccak256("\x19Ethereum Signed Message:\n" + len(msg) + msg). Returns the 65-byte signature with the recovery byte canonicalized to 27/28. Used by SIWE / EIP-4361 login flows.

func (*PrivateKeySigner) SignRaw

func (s *PrivateKeySigner) SignRaw(hash [32]byte) ([]byte, error)

SignRaw signs an arbitrary 32-byte hash with the EOA private key. Required by wrapPOLY1271Signature for the ERC-7739 envelope.

func (*PrivateKeySigner) SignTypedData

func (s *PrivateKeySigner) SignTypedData(hash [32]byte, _ [32]byte) ([32]byte, error)

type SIWEMessage

type SIWEMessage struct {
	Domain         string `json:"domain"`
	Address        string `json:"address"`
	Statement      string `json:"statement"`
	URI            string `json:"uri"`
	Version        string `json:"version"`
	ChainID        int64  `json:"chainId"`
	Nonce          string `json:"nonce"`
	IssuedAt       string `json:"issuedAt"`
	ExpirationTime string `json:"expirationTime"`
}

SIWEMessage holds the structured EIP-4361 fields. Field names match the viem JSON representation that ships to /login as the bearer payload.

func NewPolymarketSIWE

func NewPolymarketSIWE(address, nonce string, chainID int64, now time.Time) SIWEMessage

NewPolymarketSIWE builds a SIWEMessage with the Polymarket-specific domain, statement, and URI defaults. The address is checksummed; the nonce comes from `GET gamma-api.polymarket.com/nonce`. Issued-at and expiration are populated automatically with a 7-day window unless overridden.

func (SIWEMessage) String

func (m SIWEMessage) String() string

String renders the SIWEMessage in EIP-4361 plaintext form. This is the blob that gets personal_sign-hashed.

type SIWESession

type SIWESession struct {
	// contains filtered or unexported fields
}

SIWESession orchestrates the gamma-api SIWE login flow and holds the resulting session cookie in an in-memory jar. Mirrors the Polymarket frontend's `useSignIn` mutation.

Wire flow:

  1. GET {gammaURL}/nonce → { nonce: "..." }
  2. Build SIWEMessage, personal_sign with PrivateKeySigner
  3. GET {gammaURL}/login → Authorization: Bearer <token>, withCredentials → Set-Cookie
  4. Cookies persist in the jar — pass [HTTPClient] to downstream callers (relayer auth mint) so the cookies ride along.

func NewSIWESession

func NewSIWESession(signer *PrivateKeySigner, gammaURL string, opts ...SIWESessionOption) (*SIWESession, error)

NewSIWESession builds a session with a fresh in-memory cookie jar. gammaURL is typically https://gamma-api.polymarket.com (no trailing slash).

func (*SIWESession) CookiesFor

func (s *SIWESession) CookiesFor(rawURL string) []*http.Cookie

CookiesFor returns the cookies the jar would attach to a request to rawURL. Useful for diagnostics and for callers that need to forward cookies to a different transport.

func (*SIWESession) HTTPClient

func (s *SIWESession) HTTPClient() *http.Client

HTTPClient returns the underlying http.Client (with the populated jar). Pass this to downstream calls — e.g. the relayer auth mint — so cookies captured by [Login] ride along automatically.

func (*SIWESession) Login

func (s *SIWESession) Login(ctx context.Context) error

Login runs the full SIWE flow. On success the polymarket session cookie is in the session's jar.

type SIWESessionOption

type SIWESessionOption func(*SIWESession)

SIWESessionOption configures a SIWESession at construction time.

func WithSIWEClock

func WithSIWEClock(now func() time.Time) SIWESessionOption

WithSIWEClock sets the clock used for `issuedAt` / `expirationTime`. Tests pass a fixed time so the SIWE message is deterministic.

func WithSIWEHTTPClient

func WithSIWEHTTPClient(client *http.Client) SIWESessionOption

WithSIWEHTTPClient lets the caller supply a pre-configured *http.Client (with custom Timeout, Transport, etc). The cookie jar is preserved.

type Signer

type Signer interface {
	// Address returns the checksummed Ethereum address.
	Address() string

	// ChainID returns the configured chain identifier.
	ChainID() int64

	// SignHash signs a 32-byte hash (personal_sign style).
	SignHash(hash [32]byte) ([]byte, error)

	// SignTypedData signs an EIP-712 typed data hash.
	SignTypedData(domainHash, structHash [32]byte) ([32]byte, error)
}

Signer is the abstraction for signing operations. Supports local private key and leaves room for KMS/Turnkey/HSM implementations.

type Status

type Status struct {
	AccessLevel   AccessLevel `json:"access_level"`
	HasSigner     bool        `json:"has_signer"`
	HasAPIKey     bool        `json:"has_api_key"`
	HasBuilder    bool        `json:"has_builder"`
	SignerAddress string      `json:"signer_address,omitempty"`
	ChainID       int64       `json:"chain_id"`
	SignatureType string      `json:"signature_type"`
}

Status reports the current auth readiness without exposing secrets.

type TypedData

type TypedData struct {
	Types       TypedDataTypes         `json:"types"`
	PrimaryType string                 `json:"primaryType"`
	Domain      EIP712Domain           `json:"domain"`
	Message     map[string]interface{} `json:"message"`
}

TypedData is the top-level EIP-712 structure.

func TypedDataFromClobAuth

func TypedDataFromClobAuth(domain EIP712Domain, msg ClobAuthMessage) TypedData

TypedDataFromClobAuth builds the full EIP-712 TypedData from a ClobAuth.

func (TypedData) MarshalJSON

func (td TypedData) MarshalJSON() ([]byte, error)

MarshalJSON serializes the full typed data for signing via personal_sign.

type TypedDataField

type TypedDataField struct {
	Name string `json:"name"`
	Type string `json:"type"`
}

type TypedDataTypes

type TypedDataTypes map[string][]TypedDataField

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL