httprust

Can't figure out how to send a signed POST request to OKEx


I want to send a signed POST request to Okex: Authentication Docs POST Request Docs.

I always get back an "invalid sign" error.

I successfully sent a signed GET request. For the POST you also need to add the body in the signature. If I do that, none of my signatures are valid anymore. I already verified that my signature is the same as one produced by their official Python SDK (that's why I wrote the JSON by hand. Python has spaces in the JSON). I am new to Rust so I am hoping I am missing something obvious.

OKEx client implementations in other languages: https://github.com/okcoin-okex/open-api-v3-sdk

/// [dependencies]
/// hmac="0.7.1"
/// reqwest = "0.9.18"
/// chrono = "0.4.6"
/// base64="0.10.1"
/// sha2="0.8.0"

use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
use chrono::prelude::{Utc, SecondsFormat};
use hmac::{Hmac, Mac};
use sha2::{Sha256};

static API_KEY: &'static str = "<insert your key!>";
static API_SECRET: &'static str = "<insert your secret!>";
static PASSPHRASE: &'static str = "<insert your passphrase!>";

fn main() {
    let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
    let method = "POST";
    let request_path = "/api/spot/v3/orders";

    let body_str = "{\"type\": \"market\", \"side\": \"sell\", \"instrument_id\": \"ETH-USDT\", \"size\": \"0.001\"}";
    let mut signature_content = String::new();
    signature_content.push_str(&timestamp);
    signature_content.push_str(method);
    signature_content.push_str(request_path);
    signature_content.push_str(&body_str);

    type HmacSha256 = Hmac<Sha256>;
    let mut mac = HmacSha256::new_varkey(API_SECRET.as_bytes()).unwrap();
    mac.input(signature_content.as_bytes());
    let signature = mac.result().code();
    let base64_signature = base64::encode(&signature);

    let mut header_map = HeaderMap::new();
    header_map.insert("OK-ACCESS-KEY", HeaderValue::from_str(API_KEY).unwrap());
    header_map.insert("OK-ACCESS-SIGN", HeaderValue::from_str(&base64_signature).unwrap());
    header_map.insert("OK-ACCESS-TIMESTAMP", HeaderValue::from_str(&timestamp).unwrap());
    header_map.insert("OK-ACCESS-PASSPHRASE", HeaderValue::from_str(PASSPHRASE).unwrap());
    header_map.insert(CONTENT_TYPE, HeaderValue::from_static("application/json; charset=UTF-8"));

    let client = reqwest::Client::new();
    let mut complete_url = String::from("https://okex.com");
    complete_url.push_str(request_path);

    let res = client
        .post(complete_url.as_str())
        .headers(header_map)
        .body(body_str)
        .send().unwrap().text();

    println!("{:#?}", res);
}

This returns an "Invalid Sign" error at the moment but should return a successful http code (if enough funds are on the account).


Solution

  • Solution was to use "https://www.okex.com" instead of "https://okex.com. The latter produces the "Invalid Sign" error. But just for POST requests. Issue was therefore not Rust related.