Comment on page
Rust
Run the following Cargo command in your project directory:
cargo add shadow-drive-sdk
Or add the following line to your Cargo.toml
shadow-drive-sdk = "0.6.1"
This Rust code example demonstrates how to upload multiple files to a ShdwDrive using the
shadow_drive_rust
library. It initializes a tracing subscriber, reads a keypair from a file, creates a ShdwDrive client, derives the storage account public key, reads files from a directory, creates a vector of ShadowFile
structs for upload, and finally uploads the files to the ShdwDrive.// Example - Upload Multiple Files to ShdwDrive Using Rust
// Initialize the tracing.rs subscriber with environment filter
tracing_subscriber::fmt()
.with_env_filter("off,shadow_drive_rust=debug")
.init();
​
// Load keypair from file using the provided KEYPAIR_PATH
let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
​
// Create a new ShdwDriveClient instance with the loaded keypair and server URL
let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
​
// Derive the storage account public key using the keypair's public key
let pubkey = keypair.pubkey();
let (storage_account_key, _) =
shadow_drive_rust::derived_addresses::storage_account(&pubkey, 0);
​
// Read files from the "multiple_uploads" directory
let dir = tokio::fs::read_dir("multiple_uploads")
.await
.expect("failed to read multiple uploads dir");
​
// Create a Vec of ShadowFile structs for upload
// by iterating through the directory entries
let mut files = tokio_stream::wrappers::ReadDirStream::new(dir)
.filter(Result::is_ok)
.and_then(|entry| async move {
Ok(ShadowFile::file(
entry
.file_name()
.into_string()
.expect("failed to convert os string to regular string"),
entry.path(),
))
})
.collect::<Result<Vec<_>, _>>()
.await
.expect("failed to create shdw files for dir");
​
// Add a ShadowFile with bytes content to the files vector
files.push(ShadowFile::bytes(
String::from("buf.txt"),
&b"this is a buf test"[..],
));
​
// Upload the files to the ShdwDrive using the storage_account_key
let upload_results = shdw_drive_client
.upload_multiple_files(&storage_account_key, files)
.await
.expect("failed to upload files");
​
//profit
println!("upload results: {:#?}", upload_results);
Adds storage capacity to the specified immutable
StorageAccount
. This will fail if the StorageAccount
is not immutable.storage_account_key
- The public key of the immutableStorageAccount
.size
- The additional amount of storage you want to add. E.g if you have an existing StorageAccount with 1MB of storage but you need 2MB total, size should equal 1MB. When specifying size, only KB, MB, and GB storage units are currently supported.
let add_immutable_storage_response = shdw_drive_client
.add_immutable_storage(storage_account_key, Byte::from_str("1MB").expect("invalid byte string"))
.await?;
{
message: String,
transaction_signature: String,
error: Option<String>,
}
Adds storage capacity to the specified StorageAccount.
storage_account_key
- The public key of the StorageAccount.size
- The additional amount of storage you want to add. E.g if you have an existing StorageAccount with 1MB of storage but you need 2MB total, size should equal 1MB. When specifying size, only KB, MB, and GB storage units are currently supported.
let add_immutable_storage_response = shdw_drive_client
.add_immutable_storage(storage_account_key, Byte::from_str("1MB").expect("invalid byte string"))
.await?;
{
message: String,
transaction_signature: String,
error: Option<String>,
}
Unmarks a StorageAccount for deletion from the ShdwDrive. To prevent deletion, this method must be called before the end of the Solana epoch in which
delete_storage_account
is called.storage_account_key
- The public key of theStorageAccount
that you want to unmark for deletion.
let cancel_delete_storage_account_response = shdw_drive_client
.cancel_delete_storage_account(&storage_account_key)
.await?;
{
txid: String,
}
Claims any available stake as a result of the
reduce_storage
command. After reducing storage amount, users must wait until the end of the epoch to successfully claim their stake.storage_account_key
- The public key of the StorageAccount that you want to claim excess stake from.
let claim_stake_response = shdw_drive_client
.claim_stake(&storage_account_key)
.await?;
{
txid: String,
}
Creates a
StorageAccount
on the ShdwDrive. StorageAccount
s can hold multiple files and are paid for using the SHDW token.name
- The name of theStorageAccount
. Does not need to be unique.size
- The amount of storage theStorageAccount
should be initialized with. When specifying size, only KB, MB, and GB storage units are currently supported.
// Rust SDK example of creating a StorageAccount using create_storage_account
async fn main() {
// Get keypair
let keypair_file: String = std::env::args()
.skip(1)
.next()
.expect("no keypair file provided");
let keypair: Keypair = read_keypair_file(keypair_file).expect("failed to read keypair file");
println!("loaded keypair");
​
// Initialize client
let client = ShadowDriveClient::new(keypair, SOLANA_MAINNET_RPC);
println!("initialized client");
​
// Create account
let response = client
.create_storage_account(
"test",
Byte::from_bytes(2_u128.pow(20)),
shadow_drive_sdk::StorageAccountVersion::V2,
).await?;
Ok(())
}
{
message: String,
transaction_signature: String,
storage_account_address: String,
error: Option<String>,
}
Marks a file for deletion from the ShdwDrive. Files marked for deletion are deleted at the end of each Solana epoch. Marking a file for deletion can be undone with cancel_delete_file, but this must be done before the end of the Solana epoch.
storage_account_key
- The public key of theStorageAccount
that contains the file.url
- The ShdwDrive url of the file you want to mark for deletion.
let delete_file_response = shdw_drive_client
.delete_file(&storage_account_key, url)
.await?;
// Rust SDK example of marking a file for deletion from ShdwDrive using delete_file
async fn main() {
// Get keypair
let keypair_file: String = std::env::args()
.skip(1)
.next()
.expect("no keypair file provided");
let keypair: Keypair = read_keypair_file(keypair_file).expect("failed to read keypair file");
println!("loaded keypair");
​
// Initialize client
let client = ShadowDriveClient::new(keypair, SOLANA_MAINNET_RPC);
println!("initialized client");
​
// Create account
let response = client
.create_storage_account(
"test",
Byte::from_bytes(2_u128.pow(20)),
shadow_drive_sdk::StorageAccountVersion::V2,
)
.await
.expect("failed to create storage account");
let account = Pubkey::from_str(&response.shdw_bucket.unwrap()).unwrap();
println!("created storage account");
​
// Upload files
let files: Vec<ShadowFile> = vec![
ShadowFile::file("alpha.txt".to_string(), "./examples/files/alpha.txt"),
ShadowFile::file(
"not_alpha.txt".to_string(),
"./examples/files/not_alpha.txt",
),
];
let response = client
.store_files(&account, files.clone())
.await
.expect("failed to upload files");
println!("uploaded files");
for url in &response.finalized_locations {
println!(" {url}")
}
​
// Try editing
for file in files {
let response = client
.edit_file(&account, file)
.await
.expect("failed to upload files");
assert!(!response.finalized_location.is_empty(), "failed edit");
println!("edited file: {}", response.finalized_location);
}
​
// Delete files
for url in response.finalized_locations {
client
.delete_file(&account, url)
.await
.expect("failed to delete files");
}
}
This function marks a StorageAccount for deletion from the ShdwDrive. If an account is marked for deletion, all files within the account will be deleted as well. Any stake remaining in the StorageAccount will be refunded to the creator. Accounts marked for deletion are deleted at the end of each Solana epoch.
storage_account_key
- The public key of the StorageAccount that you want to mark for deletion.
- This method returns success if it can successfully mark the account for deletion and refund any remaining stake in the account before the end of the current Solana epoch.
let delete_storage_account_response = shdw_drive_client
.delete_storage_account(&storage_account_key)
.await?;
Replace an existing file on the ShdwDrive with the given updated file.
storage_account_key
- The public key of theStorageAccount
that contains the file.url
- The ShdwDrive url of the file you want to replace.data
- The updatedShadowFile
.
let edit_file_response = shdw_drive_client
.edit_file(&storage_account_key, url, file)
.await?;
{
finalized_location: String,
error: String,
}
File: examples/end_to_end.rs, Line 53
// Rust SDK end to end example of getting a keypair, initializing a client,
// creating an account, uploading a file, and editing the file
async fn main() {
// Get keypair
let keypair_file: String = std::env::args()
.skip(1)
.next()
.expect("no keypair file provided");
let keypair: Keypair = read_keypair_file(keypair_file).expect("failed to read keypair file");
println!("loaded keypair");
​
// Initialize client
let client = ShadowDriveClient::new(keypair, SOLANA_MAINNET_RPC);
println!("initialized client");
​
// Create account
let response = client
.create_storage_account(
"test",
Byte::from_bytes(2_u128.pow(20)),
shadow_drive_sdk::StorageAccountVersion::V2,
)
.await
.expect("failed to create storage account");
let account = Pubkey::from_str(&response.shdw_bucket.unwrap()).unwrap();
println!("created storage account");
​
// Upload files
let files: Vec<ShadowFile> = vec![
ShadowFile::file("alpha.txt".to_string(), "./examples/files/alpha.txt"),
ShadowFile::file(
"not_alpha.txt".to_string(),
"./examples/files/not_alpha.txt",
),
];
let response = client
.store_files(&account, files.clone())
.await
.expect("failed to upload files");
println!("uploaded files");
for url in &response.finalized_locations {
println!(" {url}")
}
// Try editing
for file in files {
let response = client
.edit_file(&account, file)
.await
.expect("failed to upload files");
}
}
Retrieve object data
Returns the
StorageAccount
associated with the pubkey provided by a user.key
- The public key of theStorageAccount
.
let storage_account = shdw_drive_client
.get_storage_account(&storage_account_key)
.await
.expect("failed to get storage account");
{
storage_account: Pubkey,
reserved_bytes: u64,
current_usage: u64,
immutable: bool,
to_be_deleted: bool,
delete_request_epoch: u32,
owner_1: Pubkey,
owner_2: Pubkey,
account_counter_seed: u32,
creation_time: u32,
creation_epoch: u32,
last_fee_epoch: u32,
identifier: String,
}
{
storage_account: Pubkey,
reserved_bytes: u64,
current_usage: u64,
immutable: bool,
to_be_deleted: bool,
delete_request_epoch: u32,
owner_1: Pubkey,
account_counter_seed: u32,
creation_time: u32,
creation_epoch: u32,
last_fee_epoch: u32,
identifier: String,
}
Returns all
StorageAccounts
associated with the public key provided by a user.owner
- The public key that is the owner of all the returnedStorageAccounts
.
let storage_accounts = shdw_drive_client
.get_storage_accounts(&user_pubkey)
.await
.expect("failed to get storage account");
{
​
storage_account: Pubkey,
reserved_bytes: u64,
current_usage: u64,
immutable: bool,
to_be_deleted: bool,
delete_request_epoch: u32,
owner_1: Pubkey,
owner_2: Pubkey,
account_counter_seed: u32,
creation_time: u32,
creation_epoch: u32,
last_fee_epoch: u32,
identifier: String,
}
{
storage_account: Pubkey,
reserved_bytes: u64,
current_usage: u64,
immutable: bool,
to_be_deleted: bool,
delete_request_epoch: u32,
owner_1: Pubkey,
account_counter_seed: u32,
creation_time: u32,
creation_epoch: u32,
last_fee_epoch: u32,
identifier: String,
}
This method is used to get the amount of storage currently used by a given storage account.
storage_account_key
- The public key of theStorageAccount
that owns the files.
let storage_account_size = shdw_drive_client
.get_stroage_account_size(&storage_account_key)
.await?;
{
storage_used: u64;
error: Option<String>;
}
Gets a list of all files associated with a
StorageAccount
. The output contains all of the file names as strings.storage_account_key
- The public key of theStorageAccount
that owns the files.
let files = shdw_drive_client
.list_objects(&storage_account_key)
.await?;
Note: The response is a vector containing all of the file names as strings.
Permanently locks a
StorageAccount
and all contained files. After a StorageAccount
has been locked, a user will no longer be able to delete/edit files, add/reduce storage amount, or delete the StorageAccount
.storage_account_key
- The public key of theStorageAccount
that will be made immutable.
let make_immutable_response = shdw_drive_client
.make_storage_immutable(&storage_account_key)
.await?;
{
message: String,
transaction_signature: String,
error: Option<String>,
}
Migrates a v1 StorageAccount to v2. This requires two separate transactions to reuse the original pubkey. To minimize chance of failure, it is recommended to call this method with a commitment level of Finalized.
storage_account_key
- The public key of the StorageAccount to be migrated.
let migrate_response = shdw_drive_client
.migrate(&storage_account_key)
.await?;
{
txid: String,
}
First transaction step that migrates a v1
StorageAccount
to v2. Consists of copying the existing account’s data into an intermediate account, and deleting the v1 StorageAccount
.Second transaction step that migrates a v1
StorageAccount
to v2. Consists of recreating the StorageAccount
using the original pubkey, and deleting the intermediate account.Creates a new ShadowDriveClient from the given
Signer
and URL.wallet
- ASigner
that for signs all transactions generated by the client. Typically this is a user’s keypair.rpc_url
- An HTTP URL of a Solana RPC provider. The underlying Solana RPC client is configured with 120s timeout and a commitment level of confirmed.
To customize RpcClient settings see
new_with_rpc
.use solana_sdk::signer::keypair::Keypair;
​
let wallet = Keypair::generate();
let shdw_drive = ShadowDriveClient::new(wallet, "https://ssc-dao.genesysgo.net");
examples/end_to_end.rs
(line 19)// Rust SDK example using `new` method to create a new ShadowDriveClient
async fn main() {
// Get keypair
let keypair_file: String = std::env::args()
.skip(1)
.next()
.expect("no keypair file provided");
let keypair: Keypair = read_keypair_file(keypair_file).expect("failed to read keypair file");
println!("loaded keypair");
​
// Initialize client
let client = ShadowDriveClient::new(keypair, SOLANA_MAINNET_RPC);
println!("initialized client");
}
Creates a new ShadowDriveClient from the given
Signer
and RpcClient
.wallet
- ASigner
that for signs all transactions generated by the client. Typically this is a user’s keypair.rpc_client
- A SolanaRpcClient
that handles sending transactions and reading accounts from the blockchain. Providing theRpcClient
allows customization of timeout and commitment level.
use solana_client::rpc_client::RpcClient;
use solana_sdk::signer::keypair::Keypair;
use solana_sdk::commitment_config::CommitmentConfig;
​
let wallet = Keypair::generate();
let solana_rpc = RpcClient::new_with_commitment("https://ssc-dao.genesysgo.net", CommitmentConfig::confirmed());
let shdw_drive = ShadowDriveClient::new_with_rpc(wallet, solana_rpc);
Reclaims the Solana rent from any on-chain file accounts. Older versions of the ShdwDrive used to create accounts for uploaded files.
storage_account_key
- The public key of the StorageAccount that contained the deleted file.file_account_key
- The public key of the File account to be closed.
let redeem_rent_response = shdw_drive_client
.redeem_rent(&storage_account_key, &file_account_key)
.await?;
{
message: String,
transaction_signature: String,
error: Option<String>,
}
Reduces the amount of total storage available for the given
StorageAccount
.storage_account_key
- The public key of theStorageAccount
whose storage will be reduced.size
- The amount of storage you want to remove. E.g if you have an existingStorageAccount
with 3MB of storage but you want 2MB total, size should equal 1MB. When specifying size, only KB, MB, and GB storage units are currently supported.
let reduce_storage_response = shdw_drive_client
.reduce_storage(&storage_account_key, reduced_bytes)
.await?;
{
message: String,
transaction_signature: String,
error: Option<String>,
}
This method is used to update your storage account's stake amount. It is required to call this method after calling the `topUp` method in order for your stage account to update properly.
storage_account_key
:PublicKey
- Publickey of the Storage Account
let refresh_stake = shdw_drive_client
.refresh_stake(&storage_account_key)
.await?;
{
txid: string;
}
Stores files in the specified
StorageAccount