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
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ set(POLYMARKET_CLIENT_SOURCES
src/websocket_client.cpp
src/market_fetcher.cpp
src/orderbook.cpp
src/clob_order_execution.cpp
src/order_execution.cpp
src/polymarket_events.cpp
src/order_signer.cpp
src/order_signer_v2.cpp
Expand Down Expand Up @@ -165,6 +167,11 @@ if(POLYMARKET_CLIENT_BUILD_TESTS)
add_executable(test_http_client_transport tests/test_http_client_transport.cpp)
target_link_libraries(test_http_client_transport PRIVATE polymarket::client)
add_test(NAME test_http_client_transport COMMAND test_http_client_transport)

add_executable(test_order_execution tests/test_order_execution.cpp)
target_include_directories(test_order_execution PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(test_order_execution PRIVATE polymarket::client)
add_test(NAME test_order_execution COMMAND test_order_execution)
endif()

# Install library, headers, and dependency targets into a single export set
Expand Down
243 changes: 0 additions & 243 deletions src/clob_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace polymarket
// Exchange addresses for Polygon mainnet
static const std::string EXCHANGE_ADDRESS = "0xE111180000d2663C0091e4f400237545B87B996B";
static const std::string NEG_RISK_EXCHANGE_ADDRESS = "0xe2222d279d744050d28e00520010520000310F59";
static const std::string ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";

// Data API URL for positions
static const std::string DATA_API_URL = "https://data-api.polymarket.com";
Expand Down Expand Up @@ -679,248 +678,6 @@ namespace polymarket
return false; // TODO: Implement DELETE method
}

// ============================================================
// AUTHENTICATED ENDPOINTS (L2 - Trading)
// ============================================================

SignedOrder ClobClient::create_order(const CreateOrderParams &params)
{
if (!order_signer_)
{
throw std::runtime_error("Client not authenticated");
}

// Use cached neg_risk if provided, otherwise fetch from API
bool is_neg_risk = false;
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;
}

std::string exchange_addr = is_neg_risk ? NEG_RISK_EXCHANGE_ADDRESS : EXCHANGE_ADDRESS;

// Calculate amounts
double maker_amount, taker_amount;
if (params.side == OrderSide::BUY)
{
// BUY: maker pays USDC, receives shares
maker_amount = params.size * params.price;
taker_amount = params.size;
}
else
{
// SELL: maker pays shares, receives USDC
maker_amount = params.size;
taker_amount = params.size * params.price;
}

OrderData order_data;
order_data.maker = funder_address_.empty() ? order_signer_->address() : funder_address_;
order_data.taker = ZERO_ADDRESS;
order_data.token_id = params.token_id;
order_data.maker_amount = to_wei(maker_amount, 6);
order_data.taker_amount = to_wei(taker_amount, 6);
order_data.side = params.side;
order_data.signer = sig_type_ == SignatureType::POLY_1271 ? order_data.maker : order_signer_->address();
order_data.expiration = params.expiration;
order_data.metadata = params.metadata;
order_data.builder = params.builder_code;
order_data.signature_type = sig_type_;

return order_signer_->sign_order(order_data, exchange_addr);
}

SignedOrder ClobClient::create_market_order(const CreateMarketOrderParams &params)
{
if (!order_signer_)
{
throw std::runtime_error("Client not authenticated");
}

// Get current price if not specified
double price = 0.5;
if (params.price)
{
price = *params.price;
}
else
{
auto price_info = get_price(params.token_id, params.side == OrderSide::BUY ? "buy" : "sell");
if (price_info)
{
price = price_info->price;
}
}

// Calculate size from amount
double size;
if (params.side == OrderSide::BUY)
{
size = params.amount / price;
}
else
{
size = params.amount;
}

CreateOrderParams order_params;
order_params.token_id = params.token_id;
order_params.price = price;
order_params.size = size;
order_params.side = params.side;
order_params.metadata = params.metadata;
order_params.builder_code = params.builder_code;

return create_order(order_params);
}

OrderResponse ClobClient::post_order(const SignedOrder &order, OrderType order_type)
{
json body;
body["order"] = {
{"salt", std::stoll(order.salt)},
{"maker", order.maker},
{"signer", order.signer},
{"taker", order.taker},
{"tokenId", order.token_id},
{"makerAmount", order.maker_amount},
{"takerAmount", order.taker_amount},
{"expiration", order.expiration},
{"side", order.side == 0 ? "BUY" : "SELL"},
{"signatureType", order.signature_type},
{"timestamp", order.timestamp},
{"metadata", order.metadata},
{"builder", order.builder},
{"signature", order.signature}};
body["owner"] = api_creds_ ? api_creds_->api_key : "";
body["orderType"] = order_type_to_string(order_type);
body["deferExec"] = false;
body["postOnly"] = false;

std::string body_str = body.dump();
auto headers = get_l2_headers("POST", "/order", body_str);
auto response = http_.post("/order", body_str, headers);

return parse_order_response(response.body);
}

std::vector<OrderResponse> ClobClient::post_orders(const std::vector<BatchOrderEntry> &orders)
{
std::vector<OrderResponse> results;

if (orders.empty())
return results;

json body = json::array();
for (const auto &entry : orders)
{
json order_json;
order_json["order"] = {
{"salt", std::stoll(entry.order.salt)},
{"maker", entry.order.maker},
{"signer", entry.order.signer},
{"taker", entry.order.taker},
{"tokenId", entry.order.token_id},
{"makerAmount", entry.order.maker_amount},
{"takerAmount", entry.order.taker_amount},
{"expiration", entry.order.expiration},
{"side", entry.order.side == 0 ? "BUY" : "SELL"},
{"signatureType", entry.order.signature_type},
{"timestamp", entry.order.timestamp},
{"metadata", entry.order.metadata},
{"builder", entry.order.builder},
{"signature", entry.order.signature}};
order_json["owner"] = api_creds_ ? api_creds_->api_key : "";
order_json["orderType"] = order_type_to_string(entry.order_type);
order_json["deferExec"] = false;
order_json["postOnly"] = false;
body.push_back(order_json);
}

std::string body_str = body.dump();
auto headers = get_l2_headers("POST", "/orders", body_str);
auto response = http_.post("/orders", body_str, headers);

try
{
auto j = json::parse(response.body);
if (j.is_array())
{
for (const auto &item : j)
{
results.push_back(parse_order_response(item.dump()));
}
}
}
catch (...)
{
// Single error response
results.push_back(parse_order_response(response.body));
}

return results;
}

OrderResponse ClobClient::create_and_post_order(const CreateOrderParams &params, OrderType order_type)
{
auto signed_order = create_order(params);
return post_order(signed_order, order_type);
}

OrderResponse ClobClient::create_and_post_market_order(const CreateMarketOrderParams &params, OrderType order_type)
{
auto signed_order = create_market_order(params);
return post_order(signed_order, order_type);
}

bool ClobClient::cancel_order(const std::string &order_id)
{
json body;
body["orderID"] = order_id;

std::string body_str = body.dump();
auto headers = get_l2_headers("DELETE", "/order", body_str);

// Use POST with body for cancel (API accepts this)
auto response = http_.post("/order", body_str, headers);
return response.ok();
}

bool ClobClient::cancel_orders(const std::vector<std::string> &order_ids)
{
json body = order_ids;

std::string body_str = body.dump();
auto headers = get_l2_headers("DELETE", "/orders", body_str);

auto response = http_.post("/orders", body_str, headers);
return response.ok();
}

bool ClobClient::cancel_all()
{
auto headers = get_l2_headers("DELETE", "/cancel-all", "");
auto response = http_.post("/cancel-all", "{}", headers);
return response.ok();
}

bool ClobClient::cancel_market_orders(const std::string &condition_id)
{
json body;
body["market"] = condition_id;

std::string body_str = body.dump();
auto headers = get_l2_headers("DELETE", "/cancel-market-orders", body_str);

auto response = http_.post("/cancel-market-orders", body_str, headers);
return response.ok();
}

std::optional<OpenOrder> ClobClient::get_order(const std::string &order_id)
{
auto headers = get_l2_headers("GET", "/order/" + order_id, "");
Expand Down
Loading
Loading