Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.16)
# Policy for older CMake compatibility in dependencies
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)

project(polymarket_cpp_client VERSION 1.2.4 LANGUAGES CXX C)
project(polymarket_cpp_client VERSION 1.2.5 LANGUAGES CXX C)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ include(FetchContent)
FetchContent_Declare(
polymarket_client
GIT_REPOSITORY https://github.com/SebastianBoehler/polymarket-cpp-client.git
GIT_TAG v1.2.4 # or any release tag
GIT_TAG v1.2.5 # or any release tag
)
FetchContent_MakeAvailable(polymarket_client)

Expand All @@ -53,11 +53,11 @@ Download pre-built binaries from [Releases](https://github.com/SebastianBoehler/

```bash
# macOS
curl -LO https://github.com/SebastianBoehler/polymarket-cpp-client/releases/download/v1.2.4/polymarket-cpp-client-macos-arm64.tar.gz
curl -LO https://github.com/SebastianBoehler/polymarket-cpp-client/releases/download/v1.2.5/polymarket-cpp-client-macos-arm64.tar.gz
tar -xzf polymarket-cpp-client-macos-arm64.tar.gz -C /usr/local

# Linux
curl -LO https://github.com/SebastianBoehler/polymarket-cpp-client/releases/download/v1.2.4/polymarket-cpp-client-linux-x64.tar.gz
curl -LO https://github.com/SebastianBoehler/polymarket-cpp-client/releases/download/v1.2.5/polymarket-cpp-client-linux-x64.tar.gz
tar -xzf polymarket-cpp-client-linux-x64.tar.gz -C /usr/local
```

Expand Down
1 change: 1 addition & 0 deletions include/clob_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ namespace polymarket
std::string tick_size = "0.01";
std::string metadata = "0x0000000000000000000000000000000000000000000000000000000000000000";
std::string builder_code = "0x0000000000000000000000000000000000000000000000000000000000000000";
std::optional<bool> neg_risk; // If set, skips API call to fetch neg_risk
};

// Batch order entry
Expand Down
4 changes: 2 additions & 2 deletions include/polymarket/version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#define POLYMARKET_CLIENT_VERSION_MAJOR 1
#define POLYMARKET_CLIENT_VERSION_MINOR 2
#define POLYMARKET_CLIENT_VERSION_PATCH 4
#define POLYMARKET_CLIENT_VERSION "1.2.4"
#define POLYMARKET_CLIENT_VERSION_PATCH 5
#define POLYMARKET_CLIENT_VERSION "1.2.5"

namespace polymarket
{
Expand Down
11 changes: 9 additions & 2 deletions src/clob_order_execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,15 @@ namespace polymarket
}

bool is_neg_risk = false;
auto neg_risk_info = get_neg_risk(params.token_id);
is_neg_risk = neg_risk_info && neg_risk_info->neg_risk;
if (params.neg_risk.has_value())
{
is_neg_risk = params.neg_risk.value();
}
else
{
auto neg_risk_info = get_neg_risk(params.token_id);
is_neg_risk = neg_risk_info && neg_risk_info->neg_risk;
}

const auto context = build_execution_context(*this, *order_signer_, funder_address_, sig_type_);
const auto amounts = detail::calculate_market_order_amounts(
Expand Down
144 changes: 39 additions & 105 deletions src/order_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/

#include "order_signer.hpp"
#include "order_execution.hpp"
#include "clob_client.hpp"
#include "http_client.hpp"
#include <nlohmann/json.hpp>
#include <iostream>
Expand Down Expand Up @@ -357,121 +357,55 @@ int main(int argc, char *argv[])
std::string exchange_address = is_neg_risk ? NEG_RISK_CTF_EXCHANGE : CTF_EXCHANGE;
std::cout << " Exchange: " << exchange_address << "\n";

// Calculate amounts with the same tick-size precision rules as the official clients.
double order_usd = 1.0;
const std::string live_order_type = "FAK";
const auto rounding = detail::rounding_config_for_tick_size("0.01");
detail::OrderAmounts amounts;

if (live_order_type == "GTC")
{
const double raw_price = std::floor(best_ask * 100) / 100;
const double shares = std::floor((order_usd / raw_price) * 100) / 100;
amounts = detail::calculate_limit_order_amounts(OrderSide::BUY, best_ask, shares, rounding);
}
else
if (!have_creds)
{
amounts = detail::calculate_market_order_amounts(OrderSide::BUY, order_usd, best_ask, rounding);
std::cerr << " Live mode requires API credentials\n";
http_global_cleanup();
return 1;
}

std::cout << " Placing " << live_order_type << " order: $" << amounts.maker
<< " for " << amounts.taker << " shares\n";

// Create order
OrderData real_order;
real_order.maker = funder_address;
real_order.taker = "0x0000000000000000000000000000000000000000";
real_order.token_id = yes_token;
real_order.maker_amount = to_wei(amounts.maker, 6);
real_order.taker_amount = to_wei(amounts.taker, 6);
real_order.side = OrderSide::BUY;
real_order.signer = signer.address();
real_order.expiration = "0";
// Use POLY_GNOSIS_SAFE (2) when funder != signer (proxy wallet)
real_order.signature_type = (funder_address != signer.address())
? SignatureType::POLY_GNOSIS_SAFE
: SignatureType::EOA;
const double order_usd = 1.0;
const SignatureType live_signature_type = (funder_address != signer.address())
? SignatureType::POLY_GNOSIS_SAFE
: SignatureType::EOA;
ClobClient order_client(CLOB_API, 137, private_key, creds, live_signature_type, funder_address);

CreateMarketOrderParams market_order;
market_order.token_id = yes_token;
market_order.amount = order_usd;
market_order.side = OrderSide::BUY;
market_order.price = best_ask;
market_order.tick_size = "0.01";
market_order.neg_risk = is_neg_risk;

std::cout << " Placing FAK market order: $" << order_usd << "\n";

auto real_signed = order_client.create_market_order(market_order);

// Debug: print order data before signing
std::cout << " Order data for signing:\n";
std::cout << " maker: " << real_order.maker << "\n";
std::cout << " signer: " << real_order.signer << "\n";
std::cout << " taker: " << real_order.taker << "\n";
std::cout << " tokenId: " << real_order.token_id << "\n";
std::cout << " makerAmount: " << real_order.maker_amount << "\n";
std::cout << " takerAmount: " << real_order.taker_amount << "\n";
std::cout << " side: " << static_cast<int>(real_order.side) << "\n";
std::cout << " signatureType: " << static_cast<int>(real_order.signature_type) << "\n";
std::cout << " maker: " << real_signed.maker << "\n";
std::cout << " signer: " << real_signed.signer << "\n";
std::cout << " taker: " << real_signed.taker << "\n";
std::cout << " tokenId: " << real_signed.token_id << "\n";
std::cout << " makerAmount: " << real_signed.maker_amount << "\n";
std::cout << " takerAmount: " << real_signed.taker_amount << "\n";
std::cout << " side: " << static_cast<int>(real_signed.side) << "\n";
std::cout << " signatureType: " << static_cast<int>(real_signed.signature_type) << "\n";
std::cout << " exchange: " << exchange_address << "\n";

auto real_signed = signer.sign_order(real_order, exchange_address);

// Build POST body - must match TS client's orderToJson format exactly
// Use ordered_json to preserve field order (API may be sensitive to this)
nlohmann::ordered_json post_body;
nlohmann::ordered_json order_obj;
order_obj["salt"] = std::stoll(real_signed.salt);
order_obj["maker"] = real_signed.maker;
order_obj["signer"] = real_signed.signer;
order_obj["taker"] = real_signed.taker;
order_obj["tokenId"] = real_signed.token_id;
order_obj["makerAmount"] = real_signed.maker_amount;
order_obj["takerAmount"] = real_signed.taker_amount;
order_obj["side"] = real_signed.side == 0 ? "BUY" : "SELL";
order_obj["expiration"] = real_signed.expiration;
order_obj["signatureType"] = real_signed.signature_type;
order_obj["timestamp"] = real_signed.timestamp;
order_obj["metadata"] = real_signed.metadata;
order_obj["builder"] = real_signed.builder;
order_obj["signature"] = real_signed.signature;
post_body["deferExec"] = false;
post_body["postOnly"] = false;
post_body["order"] = order_obj;
post_body["owner"] = creds.api_key;
post_body["orderType"] = live_order_type;

std::string body_str = post_body.dump();
std::cout << " Full order body:\n"
<< post_body.dump(2) << "\n";

HttpClient order_http;
order_http.set_base_url(CLOB_API);
order_http.set_timeout_ms(15000);

std::map<std::string, std::string> post_headers;
post_headers["Content-Type"] = "application/json";

// Add L2 auth headers if we have credentials
if (have_creds)
auto post_response = order_client.post_order(real_signed, OrderType::FAK);
if (post_response.success)
{
auto l2 = signer.generate_l2_headers(creds, "POST", "/order", body_str, funder_address);
post_headers["POLY_ADDRESS"] = l2.poly_address;
post_headers["POLY_SIGNATURE"] = l2.poly_signature;
post_headers["POLY_TIMESTAMP"] = l2.poly_timestamp;
post_headers["POLY_API_KEY"] = l2.poly_api_key;
post_headers["POLY_PASSPHRASE"] = l2.poly_passphrase;
std::cout << " Using L2 auth with address: " << l2.poly_address << "\n";
std::cout << "\n ✅ ORDER PLACED SUCCESSFULLY!\n";
std::cout << " Order ID: " << post_response.order_id << "\n";
std::cout << " Status: " << post_response.status << "\n";
std::cout << " Cost: $" << post_response.making_amount << "\n";
std::cout << " Shares: " << post_response.taking_amount << "\n";
}

auto post_response = order_http.post("/order", body_str, post_headers);

std::cout << "\n Order placement response: " << post_response.status_code << "\n";
std::cout << " Response: " << post_response.body << "\n";

if (post_response.ok())
else
{
auto result = json::parse(post_response.body);
if (result.contains("success") && result["success"].get<bool>())
{
std::cout << "\n ✅ ORDER PLACED SUCCESSFULLY!\n";
std::cout << " Order ID: " << result["orderID"].get<std::string>() << "\n";
if (result.contains("status"))
std::cout << " Status: " << result["status"].get<std::string>() << "\n";
if (result.contains("makingAmount"))
std::cout << " Cost: $" << result["makingAmount"].get<std::string>() << "\n";
if (result.contains("takingAmount"))
std::cout << " Shares: " << result["takingAmount"].get<std::string>() << "\n";
}
std::cerr << "\n Order placement failed: " << post_response.error_msg << "\n";
}
}

Expand Down
Loading