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
3 changes: 3 additions & 0 deletions docs/clob-v2-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Implemented changes:
creation params.
- `POLY_1271` deposit-wallet signatures use the V2 wrapper format from the
official V2 SDK.
- L1/L2 auth header generation honors the configured funder/proxy address so
API-key derivation and authenticated requests use the same wallet identity as
`POLY_1271` orders.
- `POLY_1271` signer/API-key rejections are classified as
`SdkErrorCode::DepositWalletSetup` with guidance to check the deployed
funder wallet, approvals, balance, and CLOB balance sync.
Expand Down
4 changes: 3 additions & 1 deletion include/order_signer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@ namespace polymarket

// L1 auth helpers
std::array<uint8_t, 32> hash_clob_auth_domain();
std::array<uint8_t, 32> hash_clob_auth(const std::string &timestamp, uint64_t nonce);
std::array<uint8_t, 32> hash_clob_auth(const std::string &address,
const std::string &timestamp,
uint64_t nonce);
};

// Utility functions
Expand Down
16 changes: 8 additions & 8 deletions src/order_signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,15 @@ namespace polymarket
return keccak256(encoded);
}

std::array<uint8_t, 32> OrderSigner::hash_clob_auth(const std::string &timestamp, uint64_t nonce)
std::array<uint8_t, 32> OrderSigner::hash_clob_auth(const std::string &address,
const std::string &timestamp,
uint64_t nonce)
{
auto type_hash = keccak256(std::string(
"ClobAuth(address address,string timestamp,uint256 nonce,string message)"));

// Encode address (padded to 32 bytes)
auto addr_bytes = from_hex(address_);
auto addr_bytes = from_hex(address);
std::vector<uint8_t> addr_padded(32, 0);
std::memcpy(addr_padded.data() + 12, addr_bytes.data(), std::min(addr_bytes.size(), size_t(20)));

Expand Down Expand Up @@ -369,17 +371,16 @@ namespace polymarket
auto now = std::chrono::system_clock::now();
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
std::string ts_str = std::to_string(timestamp);
const std::string auth_address = override_address.empty() ? address_ : override_address;

auto domain_hash = hash_clob_auth_domain();
auto struct_hash = hash_clob_auth(ts_str, nonce);
auto struct_hash = hash_clob_auth(auth_address, ts_str, nonce);
auto message_hash = encode_eip712(domain_hash, struct_hash);

std::string signature = sign_hash(message_hash);

L1Headers headers;
// Always use signer address for L1 auth (even for proxy wallets)
// The override_address parameter is ignored for L1 - it's only for reference
headers.poly_address = address_;
headers.poly_address = auth_address;
headers.poly_signature = signature;
headers.poly_timestamp = ts_str;
headers.poly_nonce = std::to_string(nonce);
Expand Down Expand Up @@ -493,8 +494,7 @@ namespace polymarket
std::string signature = base64_encode(hmac_vec, true);

L2Headers headers;
// Always use signer address for L2 auth - the API key is associated with the signer
headers.poly_address = address_;
headers.poly_address = funder_address.empty() ? address_ : funder_address;
headers.poly_timestamp = std::to_string(timestamp);
headers.poly_api_key = creds.api_key;
headers.poly_passphrase = creds.api_passphrase;
Expand Down
17 changes: 17 additions & 0 deletions tests/test_order_signer_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,23 @@ int main()
return 1;
}

constexpr const char *kFunder = "0x1111111111111111111111111111111111111111";
const auto l1_headers = signer.generate_l1_headers(0, kFunder);
if (!expect_equal("L1 auth funder address", l1_headers.poly_address, kFunder))
{
return 1;
}

ApiCredentials creds;
creds.api_key = "test-key";
creds.api_secret = "c2VjcmV0";
creds.api_passphrase = "test-passphrase";
const auto l2_headers = signer.generate_l2_headers(creds, "GET", "/orders", "", kFunder);
if (!expect_equal("L2 auth funder address", l2_headers.poly_address, kFunder))
{
return 1;
}

const auto eoa = signer.sign_order_with_salt(base_order(SignatureType::EOA), kExchangeV2, "123456789");
if (!expect_equal("EOA V2 signature", eoa.signature,
"0x92daffe6e8b80fb13506e91647e066ff58d3f7050021043fac41a24990b279e33f3624578b7f0fc5a59b225d2d96f063c09147308e936d5a42527e369aff3d4a1c"))
Expand Down
Loading