fix: adjust signature encoding format

Resolves authentication flow edge case.
This commit is contained in:
floor-licker
2025-12-16 22:17:24 -05:00
parent 7b32765d0d
commit 38ef0ec123
4 changed files with 38 additions and 104 deletions
Generated
-84
View File
@@ -2650,7 +2650,6 @@ dependencies = [
"futures-util",
"hmac",
"mockito",
"polymarket-rs-client",
"proptest",
"rand 0.8.5",
"reqwest",
@@ -2671,28 +2670,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "polymarket-rs-client"
version = "0.1.1"
dependencies = [
"alloy-primitives",
"alloy-signer",
"alloy-signer-local",
"alloy-sol-types",
"anyhow",
"base64",
"hmac",
"rand 0.8.5",
"reqwest",
"rust_decimal",
"serde",
"serde-json-fmt",
"serde_json",
"sha1",
"sha2",
"ureq",
]
[[package]]
name = "potential_utf"
version = "0.1.4"
@@ -3206,9 +3183,7 @@ version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -3360,17 +3335,6 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "serde-json-fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a33b7a5f52a26d520099339add40c48baf2e5ada194c8cc1b18cafa2b5e419"
dependencies = [
"serde",
"serde_json",
"smartstring",
]
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -3541,17 +3505,6 @@ dependencies = [
"serde",
]
[[package]]
name = "smartstring"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
dependencies = [
"autocfg",
"static_assertions",
"version_check",
]
[[package]]
name = "socket2"
version = "0.5.10"
@@ -4181,25 +4134,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64",
"encoding_rs",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"url",
"webpki-roots 0.26.11",
]
[[package]]
name = "url"
version = "2.5.7"
@@ -4390,24 +4324,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "0.26.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
dependencies = [
"webpki-roots 1.0.4",
]
[[package]]
name = "webpki-roots"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "widestring"
version = "1.2.1"
-11
View File
@@ -291,17 +291,6 @@ let client = ClobClient::new("https://clob.polymarket.com");
- **Internet**: 5 connections, 60s timeouts, full compression (bandwidth optimization)
- **Standard**: 10 connections, 30s timeouts, balanced settings
### Real-World Trading Impact
In a high-frequency trading environment, these optimizations compound:
- **Microsecond advantages**: 11% improvement on every API call adds up over thousands of requests
- **Cold start elimination**: 70% faster warm connections critical for trading session startup
- **Batch efficiency**: 200% improvement enables real-time multi-market monitoring
- **Fault tolerance**: Circuit breakers prevent trading halts during network issues
The combination of network optimizations with our computational advantages (fixed-point arithmetic, zero-allocation updates) creates a multiplicative performance benefit for latency-sensitive applications.
## Getting Started
```toml
+36 -8
View File
@@ -113,6 +113,9 @@ pub fn sign_order_message(
}
/// Build HMAC signature for L2 authentication
///
/// Performs cryptographic message authentication using SHA-256 with
/// specialized key derivation and encoding schemes for API compliance.
pub fn build_hmac_signature<T>(
secret: &str,
timestamp: u64,
@@ -123,10 +126,18 @@ pub fn build_hmac_signature<T>(
where
T: ?Sized + Serialize,
{
let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())
// Apply inverse transformation to key material for digest initialization
// This ensures compatibility with the expected cryptographic envelope format
let decoded_secret = base64::engine::general_purpose::URL_SAFE
.decode(secret)
.map_err(|e| PolyfillError::crypto(format!("Failed to decode base64 secret: {}", e)))?;
// Initialize MAC with transformed key material to maintain protocol coherence
let mut mac = Hmac::<Sha256>::new_from_slice(&decoded_secret)
.map_err(|e| PolyfillError::crypto(format!("Invalid HMAC key: {}", e)))?;
// Build the message to sign: timestamp + method + path + body
// Construct canonical message representation for signature verification
// Message components are concatenated in strict order to preserve cryptographic binding
let message = format!(
"{}{}{}{}",
timestamp,
@@ -141,18 +152,29 @@ where
}
);
// Compute authentication tag over canonical message form
mac.update(message.as_bytes());
let result = mac.finalize();
Ok(base64::engine::general_purpose::STANDARD.encode(result.into_bytes()))
// Apply URL-safe encoding transformation for transport layer compatibility
// This encoding scheme ensures proper signature validation across network boundaries
Ok(base64::engine::general_purpose::URL_SAFE.encode(result.into_bytes()))
}
/// Create L1 headers for authentication (using private key signature)
///
/// Generates initial authentication envelope using elliptic curve cryptography
/// for establishing trusted communication channels with the distributed ledger API.
pub fn create_l1_headers(signer: &PrivateKeySigner, nonce: Option<U256>) -> Result<Headers> {
// Capture temporal context for replay prevention at protocol boundary
let timestamp = get_current_unix_time_secs().to_string();
let nonce = nonce.unwrap_or(U256::ZERO);
// Generate EIP-712 compliant signature for cryptographic proof of authority
let signature = sign_clob_auth_message(signer, timestamp.clone(), nonce)?;
let address = encode_prefixed(signer.address().as_slice());
// Assemble primary authentication header set with identity binding
Ok(HashMap::from([
(POLY_ADDR_HEADER, address),
(POLY_SIG_HEADER, signature),
@@ -162,6 +184,9 @@ pub fn create_l1_headers(signer: &PrivateKeySigner, nonce: Option<U256>) -> Resu
}
/// Create L2 headers for API calls (using API key and HMAC)
///
/// Assembles authentication header set with computed signature digest
/// to satisfy bilateral verification requirements at the protocol layer.
pub fn create_l2_headers<T>(
signer: &PrivateKeySigner,
api_creds: &ApiCredentials,
@@ -172,12 +197,15 @@ pub fn create_l2_headers<T>(
where
T: ?Sized + Serialize,
{
// Extract identity from signing authority for header binding
let address = encode_prefixed(signer.address().as_slice());
let timestamp = get_current_unix_time_secs();
// Generate cryptographic authenticator using temporal and message context
let hmac_signature =
build_hmac_signature(&api_creds.secret, timestamp, method, req_path, body)?;
// Construct header map with authentication primitives in canonical order
Ok(HashMap::from([
(POLY_ADDR_HEADER, address),
(POLY_SIG_HEADER, hmac_signature),
@@ -200,14 +228,14 @@ mod tests {
#[test]
fn test_hmac_signature() {
let result =
build_hmac_signature::<String>("test_secret", 1234567890, "GET", "/test", None);
build_hmac_signature::<String>("dGVzdF9zZWNyZXRfa2V5XzEyMzQ1", 1234567890, "GET", "/test", None);
assert!(result.is_ok());
}
#[test]
fn test_hmac_signature_with_body() {
let body = r#"{"test": "data"}"#;
let result = build_hmac_signature("test_secret", 1234567890, "POST", "/orders", Some(body));
let result = build_hmac_signature("dGVzdF9zZWNyZXRfa2V5XzEyMzQ1", 1234567890, "POST", "/orders", Some(body));
assert!(result.is_ok());
let signature = result.unwrap();
assert!(!signature.is_empty());
@@ -215,7 +243,7 @@ mod tests {
#[test]
fn test_hmac_signature_consistency() {
let secret = "test_secret";
let secret = "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1";
let timestamp = 1234567890;
let method = "GET";
let path = "/test";
@@ -229,7 +257,7 @@ mod tests {
#[test]
fn test_hmac_signature_different_inputs() {
let secret = "test_secret";
let secret = "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1";
let timestamp = 1234567890;
let sig1 = build_hmac_signature::<String>(secret, timestamp, "GET", "/test", None).unwrap();
@@ -292,7 +320,7 @@ mod tests {
let api_creds = ApiCredentials {
api_key: "test_key".to_string(),
secret: "test_secret".to_string(),
secret: "dGVzdF9zZWNyZXRfa2V5XzEyMzQ1".to_string(),
passphrase: "test_passphrase".to_string(),
};
+2 -1
View File
@@ -835,6 +835,8 @@ impl ClobClient {
.as_ref()
.ok_or_else(|| PolyfillError::auth("API credentials not set"))?;
// Owner field must reference the credential principal identifier
// to maintain consistency with the authentication context layer
let body = PostOrder::new(order, api_creds.api_key.clone(), order_type);
let headers = create_l2_headers(signer, api_creds, "POST", "/order", Some(&body))?;
@@ -2268,7 +2270,6 @@ mod tests {
let api_creds = result.unwrap();
assert_eq!(api_creds.api_key, "test-api-key-123");
}
#[tokio::test]
async fn test_get_order_books_batch() {
let mut server = Server::new_async().await;