hyperspace-rs 0.2.0

A Rust library to interact with [Hyperspace](https://avax.hyperspace.xyz/) NFT marketplace on Avalanche
Documentation
use ethers::{
    core::k256::ecdsa::SigningKey,
    middleware::SignerMiddleware,
    providers::{Http, Provider},
    signers::{LocalWallet, Signer, Wallet},
    types::U256,
};
use reqwest::{
    header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE},
    Client,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use url::Url;

mod account;
mod bindings;
mod collection;
mod constants;
mod errors;
mod execute;
pub mod types;

use constants::{
    AVALANCHE_MAINNET_CHAIN_ID, DEFAULT_AVALANCHE_C_CHAIN_RPC_URL, HYPERSPACE_API_URL,
};
use errors::HyperspaceClientError;

type HyperspaceSignerMiddleware = SignerMiddleware<Provider<Http>, Wallet<SigningKey>>;

#[derive(Debug, Clone)]
pub struct HyperspaceClient {
    http_client: Client,
    signer: Arc<HyperspaceSignerMiddleware>,
}

impl HyperspaceClient {
    pub fn new(
        api_key: impl AsRef<str>,
        private_key: impl AsRef<str>,
        rpc_url: Option<impl AsRef<str>>,
    ) -> Result<Self, HyperspaceClientError> {
        if api_key.as_ref().is_empty() {
            return Err(HyperspaceClientError::InvalidApiKey);
        }
        if private_key.as_ref().is_empty() {
            return Err(HyperspaceClientError::InvalidPrivateKey);
        };
        let rpc_url = match rpc_url {
            Some(rpc_url) => match rpc_url.as_ref().is_empty() {
                true => return Err(HyperspaceClientError::InvalidRpcUrl),
                false => rpc_url.as_ref().to_string(),
            },
            None => DEFAULT_AVALANCHE_C_CHAIN_RPC_URL.to_string(),
        };
        if rpc_url.is_empty() {
            return Err(HyperspaceClientError::InvalidRpcUrl);
        };

        let mut headers = HeaderMap::new();
        headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
        headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
        headers.insert(AUTHORIZATION, HeaderValue::from_str(api_key.as_ref())?);
        let http_client = Client::builder().default_headers(headers).build()?;

        let wallet = LocalWallet::from_bytes(&hex::decode(private_key.as_ref())?)?
            .with_chain_id(AVALANCHE_MAINNET_CHAIN_ID);
        let provider = Provider::new(Http::new(Url::parse(&rpc_url)?));
        let signer = SignerMiddleware::new(provider, wallet);

        Ok(Self {
            http_client,
            signer: Arc::new(signer),
        })
    }

    async fn get<Q, R>(
        &self,
        endpoint: impl AsRef<str>,
        params: Option<Q>,
    ) -> Result<R, HyperspaceClientError>
    where
        Q: Serialize,
        R: for<'de> Deserialize<'de>,
    {
        self.http_client
            .get(format!("{HYPERSPACE_API_URL}{}", endpoint.as_ref()))
            .query(&params)
            .send()
            .await?
            .json::<R>()
            .await
            .map_err(HyperspaceClientError::Reqwest)
    }

    async fn post<B, R>(
        &self,
        endpoint: impl AsRef<str>,
        body: B,
    ) -> Result<R, HyperspaceClientError>
    where
        B: Serialize,
        R: for<'de> Deserialize<'de>,
    {
        self.http_client
            .post(format!("{HYPERSPACE_API_URL}{}", endpoint.as_ref()))
            .json(&body)
            .send()
            .await?
            .json::<R>()
            .await
            .map_err(HyperspaceClientError::Reqwest)
    }

    pub fn account(&self) -> account::Account {
        account::Account(self)
    }

    pub fn collection(&self) -> collection::Collection {
        collection::Collection(self)
    }

    pub fn execute(&self) -> execute::Execute {
        execute::Execute(self)
    }
}