polyoxide-relay 0.21.0

Rust client library for Polymarket Relayer API
Documentation

polyoxide-relay

Rust client library for Polymarket Relayer API.

The Relay API enables submitting gasless on-chain transactions through Polymarket's relayer service on Polygon. The relayer pays gas fees on behalf of users, supporting two wallet types:

  • Safe wallets -- Gnosis Safe multisig contracts (must be deployed before first use)
  • Proxy wallets -- lightweight proxy contracts that auto-deploy on first transaction

More information about this crate can be found in the crate documentation.

Installation

[dependencies]
polyoxide-relay = "0.17"

The relay client is not bundled into the unified polyoxide crate (which covers CLOB, Gamma, and Data). Depend on polyoxide-relay directly to use it.

Feature Flags

Feature Default Description
keychain No Enables OS keychain storage for credentials via keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service)

Authentication

Relay operations require a private key for EIP-712 transaction signing and one of two authentication schemes for relay submission:

Builder API Credentials (HMAC-SHA256)

use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};

# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let config = BuilderConfig::new(
    "your-api-key".into(),
    "your-secret".into(),
    Some("your-passphrase".into()),
);
let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
let client = RelayClient::from_account(account)?;
# let _ = client;
# Ok(())
# }

Relayer API Key (static headers)

use polyoxide_relay::{RelayClient, BuilderAccount};

# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let account = BuilderAccount::with_relayer_api_key(
    "0xprivatekey...",
    "your-relayer-api-key".into(),
    "0xyour-address".into(),
)?;
let client = RelayClient::from_account(account)?;
# let _ = client;
# Ok(())
# }

OS Keychain (feature keychain)

use polyoxide_relay::{RelayClient, BuilderAccount};

# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
// Load builder credentials from the OS keychain
let account = BuilderAccount::from_keychain()?;
let client = RelayClient::from_account(account)?;

// Or load relayer API key credentials from the OS keychain
let account = BuilderAccount::from_keychain_relayer_api_key()?;
let client = RelayClient::from_account(account)?;
# let _ = client;
# Ok(())
# }

Usage

Builder Pattern

use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig, WalletType};

# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let config = BuilderConfig::new("key".into(), "secret".into(), None);
let account = BuilderAccount::new("0xprivatekey...", Some(config))?;

let client = RelayClient::builder()?
    .with_account(account)
    .wallet_type(WalletType::Safe)
    .chain_id(137)               // Polygon mainnet (default)
    .max_concurrent(2)
    .build()?;
# let _ = client;
# Ok(())
# }

Or pull settings from environment variables (RELAYER_URL, CHAIN_ID):

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
let client = RelayClient::default_builder()?
    .with_account(account)
    .build()?;
# let _ = client;
# Ok(())
# }

Gasless Redemption

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
use alloy::primitives::U256;

# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
# let client = RelayClient::from_account(account)?;
let condition_id = [0u8; 32]; // your condition ID
let index_sets = vec![U256::from(1)];

let response = client
    .submit_gasless_redemption(condition_id, index_sets)
    .await?;

println!("Transaction ID: {}", response.transaction_id);
# Ok(())
# }

Gasless Redemption with Gas Estimation

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
# use alloy::primitives::U256;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
# let client = RelayClient::from_account(account)?;
# let condition_id = [0u8; 32];
# let index_sets = vec![U256::from(1)];
let response = client
    .submit_gasless_redemption_with_gas_estimation(
        condition_id,
        index_sets,
        true, // estimate gas via RPC simulation
    )
    .await?;
# let _ = response;
# Ok(())
# }

Execute Arbitrary Transactions

use polyoxide_relay::SafeTransaction;
use alloy::primitives::{Address, U256, Bytes};

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
# let client = RelayClient::from_account(account)?;
# let calldata: Vec<u8> = vec![];
let tx = SafeTransaction {
    to: "0x...".parse().unwrap(),
    value: U256::ZERO,
    data: Bytes::from(calldata),
    operation: 0, // CALL
};

let response = client.execute(vec![tx], None).await?;
# let _ = (response, Address::ZERO);
# Ok(())
# }

Query Operations (no auth required)

use alloy::primitives::Address;

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
# let client = RelayClient::from_account(account)?;
# let safe_address = Address::ZERO;
# let address = Address::ZERO;
// Check Safe deployment status
let deployed = client.get_deployed(safe_address).await?;

// Fetch current nonce
let nonce = client.get_nonce(address).await?;

// Query transaction status
let status = client.get_transaction("tx-id").await?;
println!("State: {}", status.state);

// Measure API latency
let latency = client.ping().await?;
println!("Relay API latency: {}ms", latency.as_millis());
# let _ = (deployed, nonce);
# Ok(())
# }

Wallet Address Derivation

# use polyoxide_relay::{RelayClient, BuilderAccount, BuilderConfig};
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
# let config = BuilderConfig::new("key".into(), "secret".into(), None);
# let account = BuilderAccount::new("0xprivatekey...", Some(config))?;
# let client = RelayClient::from_account(account)?;
// Derive expected Safe address via CREATE2
let safe_address = client.get_expected_safe()?;

// Derive expected Proxy wallet address via CREATE2
let proxy_address = client.get_expected_proxy_wallet()?;
# let _ = (safe_address, proxy_address);
# Ok(())
# }

Environment Variables

Variable Description
POLYMARKET_PRIVATE_KEY Hex-encoded private key for EIP-712 signing
BUILDER_API_KEY Builder API key (HMAC auth)
BUILDER_SECRET Builder API secret (HMAC auth)
BUILDER_PASS_PHRASE Builder API passphrase (HMAC auth, optional)
RELAYER_API_KEY Relayer API key (static auth)
RELAYER_API_KEY_ADDRESS Address associated with relayer API key
RELAYER_URL Custom relayer URL (default: https://relayer-v2.polymarket.com)
CHAIN_ID Target chain ID (default: 137 for Polygon mainnet)

API Coverage

  • Transaction Submission: Sign and submit gasless transactions via Safe or Proxy wallets
  • Gasless Redemptions: Redeem CTF positions without holding MATIC
  • Gas Estimation: Simulate redemptions against Polygon RPC for accurate gas limits
  • Nonce Management: Fetch current nonce from the relayer
  • Deployment Check: Verify whether a Safe wallet is deployed on-chain
  • Transaction Status: Query the state of submitted relay transactions
  • Wallet Derivation: Compute expected Safe and Proxy wallet addresses via CREATE2
  • Multi-Send Batching: Automatically batch multiple transactions via Gnosis Safe MultiSend

Supported Chains

Chain ID Notes
Polygon mainnet 137 Full support (Safe + Proxy)
Amoy testnet 80002 Safe only (Proxy not available)

License

Licensed under either of MIT or Apache-2.0 at your option.