use serde::de::DeserializeOwned;
use std::{collections::HashMap, time::Duration};
use serde_json::{json, Value};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{commitment_config::CommitmentConfig, signer::Signer, transaction::Transaction};
mod add_immutable_storage;
mod add_storage;
mod cancel_delete_storage_account;
mod claim_stake;
mod create_storage_account;
mod delete_file;
mod delete_storage_account;
mod edit_file;
mod get_storage_account;
mod list_objects;
mod make_storage_immutable;
mod migrate;
mod redeem_rent;
mod reduce_storage;
mod refresh_stake;
mod store_files;
mod top_up;
use crate::{
constants::SHDW_DRIVE_ENDPOINT,
error::Error,
models::{FileDataResponse, GetBucketSizeResponse, ShadowDriveResult},
};
pub use add_immutable_storage::*;
pub use add_storage::*;
pub use cancel_delete_storage_account::*;
pub use claim_stake::*;
pub use create_storage_account::*;
pub use delete_file::*;
pub use delete_storage_account::*;
pub use edit_file::*;
pub use get_storage_account::*;
pub use list_objects::*;
pub use make_storage_immutable::*;
pub use migrate::*;
pub use redeem_rent::*;
pub use reduce_storage::*;
pub use refresh_stake::*;
pub use store_files::*;
pub use top_up::*;
pub struct ShadowDriveClient<T>
where
T: Signer,
{
wallet: T,
rpc_client: RpcClient,
http_client: reqwest::Client,
}
impl<T> ShadowDriveClient<T>
where
T: Signer,
{
pub fn new<U: ToString>(wallet: T, rpc_url: U) -> Self {
let rpc_client = RpcClient::new_with_timeout_and_commitment(
rpc_url.to_string(),
Duration::from_secs(120),
CommitmentConfig::confirmed(),
);
Self {
wallet,
rpc_client,
http_client: reqwest::Client::new(),
}
}
pub fn new_with_rpc(wallet: T, rpc_client: RpcClient) -> Self {
Self {
wallet,
rpc_client,
http_client: reqwest::Client::new(),
}
}
pub async fn get_object_data(&self, location: &str) -> ShadowDriveResult<FileDataResponse> {
let response = self
.http_client
.post(format!("{}/get-object-data", SHDW_DRIVE_ENDPOINT))
.header("Content-Type", "application/json")
.json(&json!({ "location": location }))
.send()
.await?;
if !response.status().is_success() {
return Err(Error::ShadowDriveServerError {
status: response.status().as_u16(),
message: response.json::<Value>().await?,
});
}
let response = response.json::<FileDataResponse>().await?;
Ok(response)
}
pub async fn get_storage_account_size(
&self,
storage_account_key: &str,
) -> ShadowDriveResult<GetBucketSizeResponse> {
let mut bucket_query = HashMap::new();
bucket_query.insert("storageAccount", storage_account_key.to_string());
let response = self
.http_client
.get(format!("{}/storage-account-size", SHDW_DRIVE_ENDPOINT))
.query(&bucket_query)
.header("Content-Type", "application/json")
.send()
.await?;
if !response.status().is_success() {
return Err(Error::ShadowDriveServerError {
status: response.status().as_u16(),
message: response.json::<Value>().await?,
});
}
let response = response.json::<GetBucketSizeResponse>().await?;
Ok(response)
}
async fn send_shdw_txn<K: DeserializeOwned>(
&self,
uri: &str,
txn_encoded: String,
storage_used: Option<u64>,
) -> ShadowDriveResult<K> {
let body = serde_json::to_string(&json!({
"transaction": txn_encoded,
"commitment": "finalized",
"storageUsed": Some(storage_used)
}))
.map_err(Error::InvalidJson)?;
let response = self
.http_client
.post(format!("{}/{}", SHDW_DRIVE_ENDPOINT, uri))
.header("Content-Type", "application/json")
.body(body)
.send()
.await?;
if !response.status().is_success() {
return Err(Error::ShadowDriveServerError {
status: response.status().as_u16(),
message: response.json::<Value>().await?,
});
}
let response = response.json::<K>().await?;
Ok(response)
}
}
pub(crate) fn serialize_and_encode(txn: &Transaction) -> ShadowDriveResult<String> {
let serialized = bincode::serialize(txn)
.map_err(|error| Error::TransactionSerializationFailed(format!("{:?}", error)))?;
Ok(base64::encode(serialized))
}