async_kraken 0.1.1

Minimal wrapper for the Kraken exchange using async-std
Documentation
use ring::{digest, hmac};
use serde_json::{json, to_value, Value};
use std::{
    error::Error,
    time::{SystemTime, UNIX_EPOCH},
};
use surf::http::mime;

use crate::{config, KrakenResult};

pub struct KrakenClient {
    api_key: Option<String>,
    api_secret: Option<String>,
}

impl KrakenClient {
    pub fn new() -> Self {
        Self {
            api_key: None,
            api_secret: None,
        }
    }

    pub fn with_credentials(api_key: String, api_secret: String) -> Self {
        Self {
            api_key: Some(api_key),
            api_secret: Some(api_secret),
        }
    }

    pub async fn api_request(&self, method: &str, payload: Value) -> Result<Value, Box<dyn Error>> {
        let method_type = config::method_type(method);
        let endpoint = format!("/{}/{}/{}", crate::config::API_VER, method_type, method);
        let uri = format!("{}{}", crate::config::API_URL, endpoint);
        let request = match method_type {
            "public" => surf::get(uri).query(&payload)?,
            "private" => {
                if self.api_key.is_none() || self.api_secret.is_none() {
                    panic!("Trying to use private method without credentials")
                }
                let mut payload = payload.clone();
                let nonce = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_millis() as u64;

                let m = payload.as_object_mut().unwrap();
                m.insert(String::from("nonce"), json!(nonce));
                let payload = to_value(m)?;

                let sha =
                    digest::digest(&digest::SHA256, format!("{}{}", nonce, payload).as_bytes());
                let data = [endpoint.as_bytes(), sha.as_ref()].concat();

                let secret_key = match &self.api_secret {
                    Some(x) => base64::decode(x.as_bytes())?,
                    None => vec![],
                };

                let mac_key = hmac::Key::new(hmac::HMAC_SHA512, &secret_key);
                let mac = hmac::sign(&mac_key, &data);
                let sig = base64::encode(mac.as_ref());

                let api_key = match &self.api_key {
                    Some(x) => x,
                    None => "",
                };
                surf::post(uri)
                    .header("API-Key", api_key)
                    .header("API-Sign", sig)
                    .content_type(mime::JSON)
                    .body(payload)
            }
            _ => panic!("Unsupported method: {}", method),
        };

        let result = request.recv_json::<KrakenResult>().await?;
        if result.error.len() > 0 {
            Err(std::boxed::Box::new(crate::KrakenError {
                er_list: result.error,
            }))
        } else {
            Ok(result.result)
        }
    }
}