relayer

package module
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Feb 10, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

Go Polymarket Builder Relayer Client

Go Reference Go Report Card

Official docs alignment: Implements Polymarket Order Attribution (builder auth headers for leaderboard/grants) and the Relayer Client flow (gasless transactions + Safe/Proxy deployment; builder authentication required with remote signing recommended); official docs: Order Attribution, Relayer Client.

A robust, type-safe Go client library for interacting with the Polymarket Relayer infrastructure. This SDK enables developers to execute gasless meta-transactions on Polygon, seamlessly integrating with Polymarket's exchange protocol.

🚀 Features

  • Gasless Transactions: Submit transactions via Polymarket's Relayer without holding MATIC for gas.
  • Dual Wallet Support: Full support for both Gnosis Safe (Smart Account) and Proxy (EOA-like) wallet architectures.
  • Builder Attribution: Native support for the Polymarket Builder Rewards program, supporting both:
    • Local Signing: Manage API keys locally.
    • Remote Signing: Delegate signing to a secure remote service.
  • Resilient Networking: Built-in automatic retries with exponential backoff for network stability.
  • EIP-712 Compliance: Automated handling of EIP-712 typed data signing and domain separation.
  • Developer Friendly: Comprehensive error handling, context support, and helper utilities for address derivation.

📦 Installation

go get github.com/GoPolymarket/go-builder-relayer-client

🛠 Configuration

The client can be configured using a struct or environment variables. Below are the standard environment variables used in examples:

Variable Description Required Example
POLYMARKET_RELAYER_URL The HTTP endpoint of the Relayer service (prod: https://relayer-v2.polymarket.com/, staging: https://relayer-v2-staging.polymarket.dev) https://relayer-v2.polymarket.com/
CHAIN_ID Network Chain ID (137 for Polygon, 80002 for Amoy) 137
PRIVATE_KEY Your Ethereum Private Key (Hex) 0x...
BUILDER_API_KEY Builder API Key for attribution (required for local signing; omit when using remote signing) ✅* ...
BUILDER_SECRET Builder API Secret (required for local signing; omit when using remote signing) ✅* ...
BUILDER_PASS_PHRASE Builder Passphrase (required for local signing; omit when using remote signing) ✅* ...
BUILDER_REMOTE_HOST Remote signing service endpoint (optional; if set, SDK uses remote signing) https://your-signer-api.com/v1/sign-builder
BUILDER_REMOTE_TOKEN Bearer token for remote signer (optional) ...

BUILDER_API_KEY, BUILDER_SECRET, and BUILDER_PASS_PHRASE are required only for local signing.

⚡ Quick Start

1. Initialize the Client
package main

import (
    "os"
    relayer "github.com/GoPolymarket/go-builder-relayer-client"
    "github.com/GoPolymarket/go-builder-relayer-client/pkg/signer"
)

func main() {
    // 1. Create a Signer (manages your Private Key)
    pk := os.Getenv("PRIVATE_KEY")
    chainID := int64(137)
    signerInstance, _ := signer.NewPrivateKeySigner(pk, chainID)

    // 2. Configure Builder Authentication (Required by Relayer)
    builderCfg := &relayer.BuilderConfig{}
    if remoteHost := os.Getenv("BUILDER_REMOTE_HOST"); remoteHost != "" {
        builderCfg.Remote = &relayer.BuilderRemoteConfig{
            Host:  remoteHost,
            Token: os.Getenv("BUILDER_REMOTE_TOKEN"),
        }
    } else {
        builderCfg.Local = &relayer.BuilderCredentials{
            Key:        os.Getenv("BUILDER_API_KEY"),
            Secret:     os.Getenv("BUILDER_SECRET"),
            Passphrase: os.Getenv("BUILDER_PASS_PHRASE"),
        }
    }
    if !builderCfg.IsValid() {
        panic("builder authentication is required: set BUILDER_REMOTE_HOST or local BUILDER_* env vars")
    }

    // 3. Initialize the Relay Client
    // Use relayer.RelayerTxSafe for Gnosis Safe (Recommended)
    // Use relayer.RelayerTxProxy for Proxy wallets
    client, err := relayer.NewRelayClient(
        os.Getenv("POLYMARKET_RELAYER_URL"),
        chainID,
        signerInstance,
        builderCfg,
        relayer.RelayerTxSafe, 
    )
    if err != nil {
        panic(err)
    }
}
2. Execute a Transaction
import (
    "context"
    "fmt"
    "github.com/GoPolymarket/go-builder-relayer-client/pkg/types"
)

func executeTx(client *relayer.RelayClient) {
    ctx := context.Background()

    // Define the transaction (e.g., USDC Approval)
    tx := types.Transaction{
        To:    "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // USDC Contract
        Data:  "0x095ea7b3...", // Approve(spender, amount)
        Value: "0",
    }

    // Submit to Relayer
    resp, err := client.Execute(ctx, []types.Transaction{tx}, "Approve USDC")
    if err != nil {
        panic(err)
    }

    // Wait for mining
    receipt, err := resp.Wait(ctx)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Transaction confirmed! Hash: %s\n", receipt.TransactionHash)
}
3. Deploy a Safe Wallet

If you are using the SAFE mode and the user doesn't have a Safe deployed yet, you can deploy it via the Relayer:

safeAddress, _ := relayer.DeriveSafeAddress(chainID, signerInstance.Address().Hex())
isDeployed, _ := client.GetDeployed(ctx, safeAddress)
if !isDeployed {
    resp, err := client.Deploy(ctx)
    if err != nil {
        panic(err)
    }
    receipt, _ := resp.Wait(ctx)
    fmt.Printf("Safe deployed at: %s\n", receipt.ProxyAddress)
}
4. End-to-End Example (Safe deploy → execute → receipt → attribution call)

See the full example in examples/end_to_end/main.go and run it with:

go run ./examples/end_to_end

The flow includes:

  • Deterministic Safe address derivation
  • Deploy Safe if needed
  • Execute a gasless transaction and wait for receipt
  • Optional attribution-bearing call (GetTransactions) to verify builder auth is attached

📚 Core Concepts

Safe vs Proxy
  • Safe (RelayerTxSafe): Uses Gnosis Safe smart contracts. It is the modern standard for Polymarket accounts, supporting multisig features and batching. Recommended for all new integrations.
  • Proxy (RelayerTxProxy): Uses a custom proxy contract. Legacy standard, primarily supported on Polygon Mainnet (ChainID 137). Not available on Amoy Testnet.
Builder Attribution

To participate in the Polymarket Rewards program, you must sign your Relayer requests with Builder Credentials (builder authentication is required by the Relayer).

  • Local: You provide the API Key/Secret directly to the SDK.
  • Remote: For higher security, you can run a separate signing service. The SDK will send the payload to your remote host for signing, keeping your secrets isolated.
Builder Authentication (Required by Relayer)

The client injects builder headers on all Relayer requests, including:

  • POST /submit (Execute + Deploy)
  • GET /transactions (list transactions)
  • GET /transaction
  • GET /deployed
  • GET /nonce
  • GET /relay-payload

Required headers:

  • POLY_BUILDER_API_KEY
  • POLY_BUILDER_PASSPHRASE
  • POLY_BUILDER_SIGNATURE
  • POLY_BUILDER_TIMESTAMP

Local signing message format:

<timestamp><method><path><body>

where timestamp is a Unix millisecond timestamp (e.g., Date.now()), body is the JSON payload (with " quotes), and the signature is HMAC-SHA256 over that string using your Builder Secret, base64url-encoded.

When signing requests with query parameters, include the full path with query string (e.g., /deployed?address=0x...).

Debug headers (redacted example):

body := `{"foo":"bar"}`
headers, _ := builderCfg.Headers(ctx, "POST", "/submit", &body, 1700000000000)

mask := func(s string) string {
    if len(s) <= 8 {
        return "********"
    }
    return s[:4] + "..." + s[len(s)-4:]
}

for _, k := range []string{
    relayer.HeaderPolyBuilderAPIKey,
    relayer.HeaderPolyBuilderPassphrase,
    relayer.HeaderPolyBuilderTimestamp,
    relayer.HeaderPolyBuilderSignature,
} {
    fmt.Printf("%s: %s\n", k, mask(headers.Get(k)))
}

Example output (redacted):

POLY_BUILDER_API_KEY: pk_...c123
POLY_BUILDER_PASSPHRASE: pass...9xyz
POLY_BUILDER_TIMESTAMP: 1700000000000
POLY_BUILDER_SIGNATURE: JqZQ...9Q==

Remote signer payload (sent to BUILDER_REMOTE_HOST):

{
  "method": "POST",
  "path": "/submit",
  "body": "{...}"
}

Remote signer response should include the headers above (keys may be returned as either POLY_BUILDER_* or poly_builder_*), and should set POLY_BUILDER_TIMESTAMP in Unix milliseconds. See examples/remote_signer_server for a Go implementation. Start it locally with:

BUILDER_API_KEY=... BUILDER_SECRET=... BUILDER_PASS_PHRASE=... \\
  go run ./examples/remote_signer_server

Then point your client to BUILDER_REMOTE_HOST=http://localhost:8080/sign-builder.

For full details, see docs/builder-auth.md.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the Apache-2.0 License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	// #nosec G101 -- header names, not hardcoded credentials.
	HeaderPolyBuilderAPIKey = "POLY_BUILDER_API_KEY"
	// #nosec G101 -- header names, not hardcoded credentials.
	HeaderPolyBuilderPassphrase = "POLY_BUILDER_PASSPHRASE"
	HeaderPolyBuilderSignature  = "POLY_BUILDER_SIGNATURE"
	HeaderPolyBuilderTimestamp  = "POLY_BUILDER_TIMESTAMP"
)
View Source
const (
	GetNonceEndpoint          = "/nonce"
	GetRelayPayloadEndpoint   = "/relay-payload"
	GetTransactionEndpoint    = "/transaction"
	GetTransactionsEndpoint   = "/transactions"
	SubmitTransactionEndpoint = "/submit"
	GetDeployedEndpoint       = "/deployed"
)

Variables

This section is empty.

Functions

func DeriveProxyAddress

func DeriveProxyAddress(chainID int64, eoa string) (string, error)

DeriveProxyAddress returns the deterministic Proxy address for an EOA on a supported chain.

func DeriveSafeAddress

func DeriveSafeAddress(chainID int64, eoa string) (string, error)

DeriveSafeAddress returns the deterministic Safe address for an EOA on a supported chain.

func GetContractConfig

func GetContractConfig(chainID int64) (types.ContractConfig, error)

func IsProxyContractConfigValid

func IsProxyContractConfigValid(config types.ProxyContractConfig) bool

func IsSafeContractConfigValid

func IsSafeContractConfigValid(config types.SafeContractConfig) bool

func SignHMAC

func SignHMAC(secret string, message string) (string, error)

SignHMAC calculates the HMAC-SHA256 signature used for builder attribution.

Types

type BuilderConfig

type BuilderConfig struct {
	Local  *BuilderCredentials
	Remote *BuilderRemoteConfig
}

BuilderConfig holds configuration for local or remote builder attribution.

func (*BuilderConfig) Headers

func (c *BuilderConfig) Headers(ctx context.Context, method, path string, body *string, timestamp int64) (http.Header, error)

Headers returns the attribution headers for a given request.

func (*BuilderConfig) IsValid

func (c *BuilderConfig) IsValid() bool

IsValid returns true if the configuration has sufficient credentials.

type BuilderCredentials

type BuilderCredentials struct {
	Key        string
	Secret     string
	Passphrase string
}

BuilderCredentials represents builder attribution credentials.

type BuilderHTTPDoer

type BuilderHTTPDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

BuilderHTTPDoer executes HTTP requests for remote signing.

type BuilderRemoteConfig

type BuilderRemoteConfig struct {
	Host       string
	Token      string
	HTTPClient BuilderHTTPDoer
}

BuilderRemoteConfig configures a remote signing service.

type ClientRelayerTransactionResponse

type ClientRelayerTransactionResponse struct {
	TransactionID   string
	State           string
	TransactionHash string
	// contains filtered or unexported fields
}

ClientRelayerTransactionResponse wraps a relayer transaction response.

func (*ClientRelayerTransactionResponse) GetTransaction

func (*ClientRelayerTransactionResponse) Wait

type HTTPClient

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

func NewHTTPClient

func NewHTTPClient(client *http.Client) *HTTPClient

func (*HTTPClient) Do

func (c *HTTPClient) Do(ctx context.Context, method, urlStr string, opts *RequestOptions, out interface{}) error

type HTTPError

type HTTPError struct {
	StatusCode int
	Body       string
	Err        *sdkerrors.SDKError
}

func (*HTTPError) Code added in v0.1.1

func (e *HTTPError) Code() sdkerrors.ErrorCode

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) Unwrap added in v0.1.1

func (e *HTTPError) Unwrap() error

type RelayClient

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

RelayClient provides access to Polymarket relayer endpoints.

func NewRelayClient

func NewRelayClient(relayerURL string, chainID int64, signer signer.Signer, builderConfig *BuilderConfig, relayTxType types.RelayerTxType) (*RelayClient, error)

func (*RelayClient) Deploy

Deploy deploys a Safe contract.

func (*RelayClient) Execute

Execute executes a batch of transactions.

func (*RelayClient) GetDeployed

func (c *RelayClient) GetDeployed(ctx context.Context, safeAddress string) (bool, error)

func (*RelayClient) GetNonce

func (c *RelayClient) GetNonce(ctx context.Context, signerAddress string, signerType string) (types.NoncePayload, error)

func (*RelayClient) GetRelayPayload

func (c *RelayClient) GetRelayPayload(ctx context.Context, signerAddress string, signerType string) (types.RelayPayload, error)

func (*RelayClient) GetTransaction

func (c *RelayClient) GetTransaction(ctx context.Context, transactionID string) ([]types.RelayerTransaction, error)

func (*RelayClient) GetTransactions

func (c *RelayClient) GetTransactions(ctx context.Context) ([]types.RelayerTransaction, error)

func (*RelayClient) PollUntilState

func (c *RelayClient) PollUntilState(ctx context.Context, transactionID string, states []types.RelayerTransactionState, failState types.RelayerTransactionState, maxPolls int, pollFrequency time.Duration) (*types.RelayerTransaction, error)

func (*RelayClient) SetHTTPClient

func (c *RelayClient) SetHTTPClient(client *HTTPClient)

SetHTTPClient allows overriding the underlying HTTP client.

type RequestOptions

type RequestOptions struct {
	Headers http.Header
	Params  map[string]string
	Body    []byte
}

Directories

Path Synopsis
examples
basic command
end_to_end command
internal
pkg
errors
Package errors provides unified error definitions for the SDK.
Package errors provides unified error definitions for the SDK.
logger
Package logger provides a unified logging interface for the SDK.
Package logger provides a unified logging interface for the SDK.

Jump to

Keyboard shortcuts

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