Higher-level Elixir SDK for Polymarket.
This package builds on polymarket_clob, which owns the low-level CLOB
surface (auth, EIP-712 order signing, order placement, balances, order books,
prices, market metadata). Use polymarket_clob directly if you only need that
surface.
polymarket is for users who want the broader Polymarket SDK — market
discovery, normalized public data, P&L helpers, on-chain CTF and pUSD
operations, and the real-time trade feed — without bringing in the runnable
trading bot.
The package compiles, depends on polymarket_clob, exposes
Polymarket.Config for shared host configuration, and provides the first
read-only slice of Polymarket.Gamma for market discovery. Subsequent
phases will add the Data API, CTF/pUSD helpers, RTDS, and a higher-level
facade.
Implemented:
- Project metadata and Hex packaging readiness.
Polymarket.Config— shared Gamma and Data API host constants.Polymarket.HTTP— shared transport helper (host, path, params, retry, Req.Test plug). Used byGammaandData.Polymarket.Gamma— read-only Gamma API wrappers:get_markets/1— list markets with caller-provided query params (limit, offset, active, closed, archived, order, conditionId, slug, …).get_market/2— fetch a single market by Gamma id or slug.get_events/1— list events with caller-provided query params.get_event/2— fetch a single event by Gamma id or slug.
Polymarket.Data— read-only Data API wrappers:get_positions/2— current positions for a wallet address (size, avg_price, current_value, redeemable).:size_thresholdopt; default0.1matches the live bot's reconciler usage.get_activity/2— TRADE + REDEEM events for a wallet address with fee-inclusiveusdcSize.:limit(default1000),:offset.get_trades/2— public trade history for a wallet address.:limit(default20),:after,:asset_id.
Polymarket.Activity— pure normalization and join helpers (no HTTP):normalize/1— turn raw/activityrows into typedPolymarket.Activity.Eventstructs.cost_for_order/3— fee-inclusive USDC cost for a CLOB order id, joining activity ↔ fills viatransactionHash.revenue_for_order/3— fee-inclusive USDC revenue for a SELL.redemption_for_market/2— sum REDEEM payouts for aconditionId.- Returns
nilwhen no event matches; sums across multiple matching events (handles GTC partial fills correctly).
Polymarket.ABI,Polymarket.ERC20,Polymarket.CTF,Polymarket.Collateral— pure on-chain calldata builders for the fixed surfaces (ERC-20approve, CTFredeemPositions/mergePositions/splitPosition, Collateralwrap/unwrap). Validated byte-for-byte against ethers.js v6 intest/fixtures/calldata.json.- Submit wrappers compose calldata + tx + RPC into one call:
Polymarket.ERC20.approve/2,Polymarket.CTF.redeem_positions/2,Polymarket.CTF.merge_positions/2,Polymarket.CTF.split_position/2,Polymarket.Collateral.wrap/2,Polymarket.Collateral.unwrap/2. Each takes(rpc, opts)and returns{:ok, tx_hash}or{:error, term}. Requiredoptsare caller-provided (:nonce,:gas_price,:gas_limit,:chain_id,:private_key,:contract, plus function-specific args). No auto-fetch of nonce/gas — usePolymarket.RPC.nonce/2,gas_price/1, andestimate_gas/2first. Polymarket.RLP— RLP encoder for Ethereum transaction serialization. Bytes, non-negative integers, and lists.Polymarket.Tx— legacy (type-0) EIP-155 transaction serialization and signing.new/1,signing_payload/1,signing_hash/1,sign/2,serialize/1,hash/1. Validated byte-for-byte against ethers.js v6 intest/fixtures/tx.json. EIP-1559 type-2 deferred.Polymarket.RPC— stateless JSON-RPC 2.0 client + nonce/gas helpers.new/1,call/3raw, plus typed helperschain_id/1,block_number/1,gas_price/1,nonce/2-3,estimate_gas/2,send_raw_transaction/2(accepts hex, raw binary, or a signedPolymarket.Tx). Tuple-shape errors:{:rpc_error, code, message, data},{:http_error, ...},{:transport_error, ...},{:invalid_response, ...}.Polymarket.RTDS— minimal real-time trade feed client:start_link(callback: pid)connects towss://ws-live-data.polymarket.comand subscribes to the global trades topic.- Forwards every trade as
{:polymarket_rtds, :trade, payload}to the callback PID.payloadis the raw map; pair withPolymarket.Activity.normalize/1for typed structs. - Configurable
:urland:ping_interval. - Phase 10 deliberate non-goals (deferred): backpressure / batching,
supervised reconnect strategy, topic generalization beyond trades,
and
{module, function}callback shape. See module docs.
- Compile-time integration with
polymarket_clob(path dep in this workspace; will be replaced with the published Hex version before publishing this package).
Not implemented yet:
- CTF redeem/merge/split (
Polymarket.CTF.*). - pUSD wrap/unwrap (
Polymarket.Collateral.*). - RTDS real-time trade feed (
Polymarket.RTDS.*). - Higher-level convenience facade.
All Polymarket.Gamma and Polymarket.Data wrappers return one of:
{:ok, body}on success.{:error, {:http_error, status, body}}for non-2xx responses.{:error, {:transport_error, reason}}for Req-level failures.
See Polymarket.HTTP's @moduledoc for the full options list (:host,
:plug, :timeout, :retry, :max_retries, :headers).
{:ok, markets} =
Polymarket.Gamma.get_markets(
params: [limit: 100, active: true, closed: false, order: "volume24hr"]
)
{:ok, market} = Polymarket.Gamma.get_market("will-x-happen")
{:ok, events} = Polymarket.Gamma.get_events(params: [limit: 50, active: true])address = "0x8f70343472CC7Fd382DE383723A0E6Ee02e2CAe5"
{:ok, positions} = Polymarket.Data.get_positions(address)
# `usdcSize` on TRADE events is fee-inclusive; this is the source of
# truth for P&L on completed BUY → REDEEM cycles.
{:ok, activity} = Polymarket.Data.get_activity(address, limit: 200)
{:ok, trades} = Polymarket.Data.get_trades(address, limit: 50)Polymarket.Activity is pure: it consumes whatever maps you have and does
not fetch from the network. Pair it with the wrappers above:
{:ok, activity} = Polymarket.Data.get_activity(address)
{:ok, fills} = PolymarketClob.API.Account.get_trades(client, market: market_id)
# Fee-inclusive cost (matches the wallet debit, not the CLOB fill price).
cost = Polymarket.Activity.cost_for_order(activity, order_id, fills["data"] || [])
# Sum of REDEEM payouts for a resolved market.
payout = Polymarket.Activity.redemption_for_market(activity, condition_id)
# Typed events for downstream consumers.
events = Polymarket.Activity.normalize(activity)All four functions return nil when no event matches, distinguishing
"no activity yet" from "events that summed to 0".
{:ok, _pid} = Polymarket.RTDS.start_link(callback: self())
receive do
{:polymarket_rtds, :trade, payload} ->
IO.inspect(payload, label: "rtds trade")
endThe payload is the raw map sent by Polymarket. Apply
Polymarket.Activity.normalize([payload]) if you need a typed
Polymarket.Activity.Event struct.
polymarket ← this package (Gamma, Data, CTF, pUSD, RTDS, facade)
│
└── polymarket_clob (CLOB auth, signing, orders, books, prices)
- It is not a low-level CLOB client. Use
polymarket_clobdirectly for trading, signing, and book reads. - It is not a paper trading engine. That is a separate
paper_expackage. - It is not the runnable trading bot. That is a separate
polymarket_botapplication.
After publication:
def deps do
[
{:polymarket, "~> 0.1.0"}
]
endMIT.