Only this pageAll pages
Powered by GitBook
1 of 20

shdwDrive

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Your cloud. Your token.

Download shdwDrive:

Direct Download | App Store Link (coming soon) | Play Store Link (coming soon)

Reimagining Cloud Storage

shdwDrive v2 transforms cloud storage into a decentralized ecosystem where users can not only store their files securely but also participate in and earn from the network. Our platform eliminates traditional centralized storage providers, replacing them with a community-driven network where everyone can contribute and benefit.

What Makes shdwDrive Different?

shdwDrive creates a marketplace where:

  • Users get reliable, secure decentralized storage

  • SHDW tokens allows to provide storage capacity

  • The community shapes the network's future

  • Everyone benefits from transparent, fairness

Want to dive deeper into how shdwDrive works? Check out our to learn about the network's economic model, proof systems, and mathematical foundations that ensure fair rewards for all participants.

How this resource is organized

Your complete resource for using shdwDrive v2 – whether you're storing files or earning rewards as a network operator. Now that we have launched our latest technology, stay tuned for frequent updates to our guides!

This section is being re-designed as we support the latest shdwDrive v2! What remains are previous versioning guides that focus on easy and actionable information to help you quickly get started building on the previous version 1.5 (soon deprecated) shdwDrive. Step by step guides, and line by line CLI instruction offer developers the quickest path to working concepts.

Much like an appendix, the reference section is a quick navigation hub for key resources. Our media kit, social media presence, and more can be found here.

Bugs and Feedback

This resource will be updated frequently and the developer community should feel empowered to for edits they would like to see or platform enhancement ideas. We also have a process for .

Read about shdwDrive v2 Economics

shdwOperators
mathematically-proven
technical deep dive
Guide
s
Build
Reference
submit Issues
Bug submissions

Get the App

Guides

Reference

SDKs

shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

Our SDK options provide the simplest means of linking your application to shdwDrive. With a variety of environments to choose from, developers can enjoy a robust and constantly evolving platform that leverages the full potential of shdwDrive. GenesysGo is dedicated to maintaining these SDKs, continuously enhancing developer capabilities, and streamlining the building process. We value your feedback and welcome any suggestions to help us improve these valuable resources.

Javascript

Rust

Python

S3-Compatible Client Access

shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

In order to use the S3-Compatible gateway for the shdwDrive network, head over to https://portal.shadow.cloud, connect your wallet and sign in, and then navigate to the storage account you'd like to generate an access key/secret key pair. Select the "Addons" tab and then you'll be able to enable & add s3-compatible key/secret pairs.

Obtaining your credentials

Once you've enabled the S3 Access addon for a given storage account, you can view and add more s3-compatible key/secret pairs by navigating to the storage account of your choosing on .

To get your credentials, simply click "View Credentials".

Permissions

Once enabled, you can manage individual permissions of each key/secret pair you generate for a given storage account. The list of permissions are configured to be compatible with existing s3 clients. Some examples of compatible s3 clients are rclone, s3cmd, and even the aws s3 cli. There's a plethora of tooling that exists for s3-compatible gateways, which is why we chose to build this into the shdwDrive network.

In general, if you want to enable uploads with one of your keys, you'll need to enable the following permissions:

You can also control if a key is able to read, which allows you to have read, write, and read+write keys for various use cases.

Gateway endpoints

In order to access the s3 compatible gateways on the shdwDrive network, you'll need to configure your s3 client to use one of the following gateways:

  1. https://us.shadow.cloud

  2. https://eu.shadow.cloud

These endpoints proxy requests to the shdwDrive network and allows you to have the best network connectivity possible given your geographical preference. All uploads are synced and available globally, so you can use either endpoint.

Example client configurations

RCLONE

The following is an example that you can add to your rclone configuration file. Typically, this is located in `~/.config/rclone/rclone.conf`.

Speed

In order to ensure the stability of the shdwDrive network, speeds are initially limited to 20 MiB/s. You can opt to upgrade the bandwidth rate limit for each individual key/secret pair you generate. The upgraded rate limit is 40 MiB/s

Security

In the event a s3 key has been compromised, you can easily rotate the key. Simply navigate to , connect your wallet, select a storage account, and then click on the "Addons" tab. From there, you can click "Rotate" button to rotate a given key/secret pair to generate a fresh pair.

Install shdwDrive

sshdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new for instructions.

Getting Started

Prerequisites: Install on any OS.

https://portal.shadow.cloud
https://portal.shadow.cloud
Then run the following command

Next steps

  • After you install ShdwDrive, learn how to use the CLI client.

  • If you prefer, here is a step by step video walkthrough.

  • Further streamline your integrations with ShdwDrive using the SDK and API.

Get Support

  • Review the FAQ

  • Visit us in Discord for support and feedback!

Submitting Bugs

We adhere to a responsible disclosure process for security related issues. To ensure the responsible disclosure and handling of security vulnerabilities, we ask that you follow the process outlined below.

Bug Reporting Process

  1. For non-security-related bugs, please submit a new bug report here. For security-related reports, please open a "security vulnerability" report here.

  2. Please provide a clear and concise description of the issue, steps to reproduce it, and any relevant screenshots or logs.

Important: For security-related issues, please include as much information as possible for reproduction and what its related to. Please be sure to use the report a security vulnerability feature in the repository listed above. If you submit a security vulnerability as a public bug report, we reserve the right to remove the report and move any communications to private channels until a resolution is made.

Security related issues should only be reported through this repository.

While we strongly encourage the use of this repository for bug reports and security issues, you may also reach out to us via our Discord server. Join the #shdw-drive-technical-support channel for assistance. However, please note that we will redirect you to submit the bug report through this GitHub repository for proper handling and tracking.

Discover benefits and use cases of ShdwDrive:

Learn more about why ShdwDrive is growing so quickly and what makes it a strong product market fit.

developer guide
NodeJS LTS 16.17.1
Get Object, Put Object, List Multipart Upload Parts, Abort Multipart upload
[shdw-cloud]
type = s3
provider = Other
access_key_id = [redacted]
secret_access_key = [redacted]
endpoint = https://us.shadow.cloud
acl = public-read
bucket_acl = public-read
npm install -g @shadow-drive/cli

Start Building

Contents

shdwDrive SDK

  • Installation

  • Local Development

shdwDrive CLI

shdwDrive SDK

shdwDrive SDK is a typeScript SDK for interacting with ShdwDrive, providing simple and efficient methods for file operations on the decentralized storage platform.

Installation

Local Development

Features

  • 📤 File uploads (supports both small and large files)

  • 📥 File deletion

  • 📋 File listing

  • 📊 Bucket usage statistics

Quick Start

Usage Examples

Upload a File

Create a Folder

Delete a Folder

List Files

Delete a File

API Reference

ShdwDriveSDK

Constructor Options

Methods

  • uploadFile(bucket: string, file: File, options?: FileUploadOptions)

  • deleteFile(bucket: string, fileUrl: string)

  • listFiles(bucket: string)

shdwDrive CLI

A command-line interface for interacting with shdwDrive storage.

Features

  • 📤 File uploads (supports both small and large files)

  • 📁 Folder support (create, delete, and manage files in folders)

  • 📥 File and folder deletion

  • 📋 File listing

Installation

You can install the CLI globally using npm:

Or use it directly from the repository:

Configuration

The CLI uses environment variables for configuration:

  • SHDW_ENDPOINT: The shdwDrive API endpoint (defaults to )

Usage

Upload a file

Delete a file

Create a folder

List files in a bucket

Check bucket storage usage

Command Options

Upload Options

  • -k, --keypair - Path to your Solana keypair file

  • -b, --bucket - Your bucket identifier

  • -f, --file - Path to the file you want to upload

Delete Options

  • -k, --keypair - Path to your Solana keypair file

  • -b, --bucket - Your bucket identifier

  • -f, --file - URL or path of the file to delete

Create Folder Options

  • -k, --keypair - Path to your Solana keypair file

  • -b, --bucket - Your bucket identifier

  • -n, --name - Name/path of the folder to create

Delete Folder Options

  • -k, --keypair - Path to your Solana keypair file

  • -b, --bucket - Your bucket identifier

  • -p, --path - Path of the folder to delete

Development

  1. Clone the repository:

  1. Install dependencies:

  1. Build the project:

  1. Link the CLI locally:

Python

shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

  • Install Python SDK

  • Examples

Installing ShdwDrive

pip install shadow-drive

Also for running the examples:

pip install solders

Check out the directory for a demonstration of the functionality.

https://shdw-drive.genesysgo.net/[STORAGE_ACCOUNT_ADDRESS]

Example - Create Account, File Upload, Add or Reduce Storage, and Delete Account using Python

About the

This package uses PyO3 to build a wrapper around the official ShdwDrive Rust SDK. For more information, see the .

Methods

Section under development.

Development
🗂️ Folder creation and management
  • 🔐 Secure message signing

  • ⚡ Progress tracking for uploads

  • 🔄 Multipart upload support for large files

  • getBucketUsage(bucket: string)
  • createFolder(bucket: string, folderName: string)

  • deleteFolder(bucket: string, folderUrl: string)

  • 📊 Bucket usage statistics
  • 🔐 Secure message signing

  • 🔄 Multipart upload support for large files

  • -F, --folder - (Optional) Folder path within the bucket
    Features
    Quick Start
    Usage Examples
    API Reference
    Features
    Installation
    Configuration
    Usage
    https://v2.shdwdrive.com
    Github
    Methods
    examples
    Github Repo
    Rust SDK documentation
    # Install from npm
    npm install @shdwdrive/sdk
    
    # Or install from repository
    git clone https://github.com/GenesysGo/shdwdrive-v2-sdk.git
    cd shdwdrive-v2-sdk
    npm install
    npm run build
    cd shdwdrive-v2-sdk
    npm link
    cd your-project
    npm link @shdw-drive/sdk
    import ShdwDriveSDK from '@shdwdrive/sdk';
    // Initialize with wallet
    const drive = new ShdwDriveSDK({}, { wallet: yourWalletAdapter });
    // Or initialize with keypair
    const drive = new ShdwDriveSDK({}, { keypair: yourKeypair });
    const file = new File(['Hello World'], 'hello.txt', { type: 'text/plain' });
    const uploadResponse = await drive.uploadFile('your-bucket', file, {
      onProgress: (progress) => {
        console.log(`Upload progress: ${progress.progress}%`);
      }
    });
    console.log('File uploaded:', uploadResponse.finalized_location);
    const folderResponse = await drive.createFolder('your-bucket', 'folder-name');
    console.log('Folder created:', folderResponse.folder_location);
    const deleteFolderResponse = await drive.deleteFolder('your-bucket', 'folder-url');
    console.log('Folder deleted:', deleteFolderResponse.success);
    const files = await drive.listFiles('your-bucket');
    console.log('Files in bucket:', files);
    const deleteResponse = await drive.deleteFile('your-bucket', 'file-url');
    console.log('Delete status:', deleteResponse.success);
    interface ShdwDriveConfig {
      endpoint?: string; // Optional custom endpoint (defaults to https://v2.shdwdrive.com)
    }
    
    // Initialize with either wallet or keypair
    new ShdwDriveSDK(config, { wallet: WalletAdapter });
    new ShdwDriveSDK(config, { keypair: Keypair });
    npm install -g @shdwdrive/cli
    git clone https://github.com/genesysgo/shdwdrive-v2-cli.git
    cd shdwdrive-v2-cli
    npm install
    npm run build
    npm link
    shdw-drive upload \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier \
      --file path/to/your/file.txt \
      --folder optional/folder/path
    # Delete a file from root of bucket
    shdw-drive delete \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier \
      --file filename.txt
    
    # Delete a file from a folder
    shdw-drive delete \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier \
      --file folder/subfolder/filename.jpg
    shdw-drive create-folder \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier \
      --name my-folder/subfolder
    shdw-drive list \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier
    shdw-drive usage \
      --keypair ~/.config/solana/id.json \
      --bucket your-bucket-identifier
    git clone https://github.com/genesysgo/shdwdrive-v2-cli.git
    cd shdwdrive-v2-cli
    npm install
    npm run build
    npm link
    from shadow_drive import ShadowDriveClient
    from solders.keypair import Keypair
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument('--keypair', metavar='keypair', type=str, required=True,
                        help='The keypair file to use (e.g. keypair.json, dev.json)')
    args = parser.parse_args()
    
    # Initialize client
    client = ShadowDriveClient(args.keypair)
    print("Initialized client")
    
    # Create account
    size = 2 ** 20
    account, tx = client.create_account("full_test", size, use_account=True)
    print(f"Created storage account {account}")
    
    # Upload files
    files = ["./files/alpha.txt", "./files/not_alpha.txt"]
    urls = client.upload_files(files)
    print("Uploaded files")
    
    # Add and Reduce Storage
    client.add_storage(2**20)
    client.reduce_storage(2**20)
    
    # Get file
    current_files = client.list_files()
    file = client.get_file(current_files[0])
    print(f"got file {file}")
    
    # Delete files
    client.delete_files(urls)
    print("Deleted files")
    
    # Delete account
    client.delete_account(account)
    print("Closed account")

    Change Logs

    Full Detail - Version Releases & Fixes:

    shdwDrive Core

    shdwDrive CLI

    shdwDrive Rust

    Consolidated Summary

    D.A.G.G.E.R. - Launch of - January 16th

    D.A.G.G.E.R. - Launch of - September 29, 2023

    shdwDrive - Release S3 Compatible Gateway - August 29, 2023

    shdwDrive

    shdwDrive Rust

    shdwDrive

    shdwDrive

    Apr 5, 2023- shdwDrive Rust

    Mar 16, 2023 - shdwDrive

    Mar 15, 2023 - shdwDrive Rust

    Feb 28, 2023 - shdwDrive Rust

    Feb 27, 2023 - shdwDrive CLI

    Feb 27, 2023 - shdwDrive

    Feb 27, 2023 - shdwDrive

    Feb 9, 2023 - shdwDrive

    Feb 8, 2023 - shdwDrive

    Feb 9, 2023 - shdwDrive CLI

    Dec 13, 2022 - shdwDrive CLI

    Nov 28, 2022 - shdwDrive

    Nov 28, 2022 - shdwDrive

    Sep 22, 2022 - shdwDrive CLI

    Sep 22, 2022 - shdwDrive CLI

    Sep 22, 2022 - shdwDrive CLI

    Sep 21, 2022 - shdwDrive CLI

    Sep 21, 2022 - shdwDrive

    Sep 21, 2022 - shdwDrive

    Sep 21, 2022 - shdwDrive CLI

    Aug 26, 2022 - Digital Asset RPC Infrastructure

    Jul 26, 2022 - shdwDrive

    Jul 22, 2022 - shdwDrive

    Jul 12, 2022 - shdwDrive

    Jul 8, 2022 - shdwDrive

    Build

    ShdwDrive Developer Tools

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

    To get started with the shdwDrive CLI:

    1. Install the

    2. Follow the

      • After installing Solana, make sure you have both SHDW and SOL in your wallet in order to reserve storage

    To get started with the shdwDrive SDKs:

    1. , , and are your choices.

    2. Follow the

      • You can build directly on top of the .

    Submitting Bugs & Security Issues

    We adhere to a responsible disclosure process for security related issues. To ensure the responsible disclosure and handling of security vulnerabilities, we ask that you follow the process outlined below.

    Bug Reporting Process

    1. For non-security-related bugs, please submit a new bug report . For security-related reports, please open a "security vulnerability" report .

    2. Please provide a clear and concise description of the issue, steps to reproduce it, and any relevant screenshots or logs.

    Important: For security-related issues, please include as much information as possible for reproduction and what its related to. Please be sure to use the report a security vulnerability feature in the repository listed above. If you submit a security vulnerability as a public bug report, we reserve the right to remove the report and move any communications to private channels until a resolution is made.

    Security related issues should only be reported through this repository.

    While we strongly encourage the use of this repository for bug reports and security issues, you may also reach out to us via our server. Join the #shdw-drive-technical-support channel for assistance. However, please note that we will redirect you to submit the bug report through this GitHub repository for proper handling and tracking.

    Social Media & Community

    These are the official social media accounts for GenesysGo and the shdwEcosystem.

    Twitter: https://twitter.com/genesysgo

    Discord: https://discord.com/invite/shdwdrive

    Blog:

    Reference

    ShdwDrive consolidated resources

    shdwDrive

    Incentivized Testnet 2
    Phase 2
    Testnet and Hammer
    Phase 1
    S3-Client Access
    v5.0.0 - August 22, 2023
    v0.7.2 - August 18, 2023
    v4.0.2 - June 27, 2023
    v4.0.1 - May 31, 2023
    v0.6.3 - Minor Fixes
    v3.3.0 - Minor Fixes
    v0.6.2 - New crates
    SDK v0.6.1 - Edit fixes
    v0.4.2 - The one that's better faster stronger
    v3.2.2 - Minor fix
    v3.2.1 - Faster better stronger
    v3.1.1 - Fix for Hardware Wallet Transaction Signing
    v3.1.0- Minor Improvements and Fixes
    v0.3.9 - Minor Bugfix Latest
    v0.3.6 - Minor fixes
    v3.0.10 - Minor fixes
    v3.0.9 - Minor improvements and fixes
    v0.3.4 - Minor fixes
    v0.3.3 - Minor fixes
    v0.3.2 - Minor fixes
    v0.3.1 - Version bump
    v3.0.8 - Minor Improvements and Fixes
    v3.0.7 - Minor Improvements and Fixes
    v0.3.0 - Open Source Release
    Custom RPC for NFT Compression
    v3.0.6 - Minor Improvements and Fixes
    v3.0.5 - Minor Fixes and Improvements
    v3.0.4 - Minor fixes
    v3.1.0- Minor Improvements and Fixes
    https://shdwdrive.com/blog
    API

    FAQ

    Support

    Network Status

    Change Log

    Version releases

    Technical Support

    shdw Glossary

    Social Media

    Discord

    Twitter

    Media Kit

    Logos and branding

    Download graphics

    CLI
    SDK JavaScript/Typescript
    SDK Rust
    SDK Python

    shdwGlossary

    A list of terms that relate to all things SHDW!

    Asynchronous Byzantine Fault Tolerance (aBFT): aBFT is a consensus algorithm used to provide fault tolerance for distributed systems within asynchronous networks. It is based on the Byzantine Fault Tolerance (BFT) algorithm and guarantees that, when properly configured and given tolerable network conditions, its consensus will eventually be reached — even when faults such as computer crashes, perfect cyber-attacks, or malicious manipulation have happened. aBFT is designed to be tolerant of malicious or faulty actors even when those actors make up more than one-third of the nodes inside the network. Blockchain: A peer to peer, decentralized, immutable digital ledger used to securely and efficiently store data that is permanently linked and secured using cryptography. Byzantine Fault Tolerance: A fault-tolerance system in distributed computing systems where multiple replicas are used to reach consensus, such that any faulty nodes can be tolerated and the consensus of the system can be maintained despite errors or malicious attacks. Consensus Algorithm: A consistency algorithm is a method of achieving agreement on a single data value across distributed systems. It is typically used in blockchain networks to arrive at a common data state and ensure consensus across network participants. It is used to achieve fault-tolerance in distributed systems and allows for the distributed system to remain operational even if some of its components fail. Consensus Protocols: A consensus protocol is an algorithm used to achieve agreement among distributed systems on the state of data within the system. Consensus protocols are essential in distributed systems to ensure there is no disagreement between the nodes on the data state, so that no node has a different view of the data. The protocols typically employ some form of voting, such as majority voting, or methods like proof of work, to achieve the necessary agreement on the data state. Cryptographic Hashing: Cryptographic hashing is a process used to convert data into a fixed-length string of characters, or "hashes", in order to protect the data and verify its authenticity through an encrypted code. The hash cannot be reversed to the original data and is used extensively time to ensure data integrity. Cryptographic hashes can be used to verify data integrity, authenticate data sources, and prevent tampering. Cryptography: The practice and study of techniques used to secure communication, data, and systems by transforming them into an unreadable format. Cryptography is an important component of cybersecurity, providing data protection and confidentiality. Data Caching: Data caching is a software engineering technique that stores frequently accessed or computational data in a cache in order to quickly access that data when it is needed. Data caching works by temporarily storing data to serve it quickly when requested. This can significantly improve the performance of applications, which reduces the amount of time spent serving requests. Data Encryption: The process of encoding data using encryption algorithms in order to keep the content secure and inaccessible to user without a decryption key. Data encryption makes it difficult for unauthorized users to read confidential data. Data Integrity: A quality of digital data that has been maintained over its entire life cycle, and is consistent and correct. The term is ensured through specific protocols such as data validation and error detection and correction. Data Partitioning: The process of splitting large data or throughputs into smaller, more manageable units. This process allows for more accuracy in data processing and faster results, as well as the ability to easily store and access the data. Data Sharding: Data Sharding is a partitioning technique which divides large datasets into smaller subsets which are stored across multiple nodes. It is used to improve scalability, availability and fault tolerance in distributed databases. When combined with replication, Data Sharding can improve the speed of queries by allowing them to run in parallel. Decentralized Storage Network: A Decentralized Storage Network is a type of distributed system which utilizes distributed nodes to store and serve data securely, without relying on a single point of failure. It is an advanced form of data storage and sharing which provides scalability and redundancy, allowing for more secure and reliable access than that offered by conventional centralized systems. Delegated Proof-of-Stake (DPoS): Delegated Proof-of-Stake (DPoS) is a consensus mechanism used in some blockchain networks. It works by allowing token holders to vote for a “delegate” of their choice to validate transactions and produce new blocks on their behalf. Delegates are rewarded for their work with a percentage of all transaction fees and new block rewards. DPoS networks are usually faster and more scalable than traditional PoW and PoS networks. Digital Signatures: A digital signature is a mathematical scheme for demonstrating the authenticity of digital messages of documents. It is used for authenticating the source of messages or documents and for providing the integrity of messages or documents. A valid digital signature gives a recipient reason to believe that the message or document was created or approved by a known sender, and has not been altered in transit. Directed Acyclic Graphs: A Directed Acyclic Graph (DAG) is a type of graph with directed edges that do not form a cycle. It consists of vertices (or nodes) and edges that connect the nodes. The direction of the edges between the nodes determines the flow of information from one node to another. A DAG is a useful structure for modeling data sets, such as queues and trees, to represent complex algorithms and processes in computer engineering. Distributed Computing: A type of computing in which different computing resources, such as memory, hard disk space, and processor time are divided between different computers working as a single system. This gives the benefits of distributed computing like scalability, load balancing, reduced latency, and improved resiliency. It is widely used in data centers and cloud computing. Distributed Consensus: The process of having a distributed system come to agreement on the state of an issue or order of operations. This is achieved through communication, verification and agreement from each of the connected nodes in the system. Distributed Database: A distributed database is a type of database which stores different parts of its data across multiple devices that are connected to computer networks, allowing for more efficient data access by multiple users and more efficient transfer of data between different locations. Distributed File System: A distributed file system is a file system that allows multiple computers to access and share files located on remote servers. It enables a computing system that consists of multiple computers to work together in order to store and manage data. The system can be seen as a large-scale, decentralized file storage platform that spans multiple nodes. Data is replicated across the computers so that if one computer goes down, another can take its place, which helps provide for high availability, scalability, and fault tolerance. Distributed Ledger Technology: A type of database technology that maintains a continuously growing list of records, each stored as a block and protected by cryptography. It is a database shared across multiple nodes in a network, that keeps track of digital transactions between the nodes and is protected by airtight security and tamper proof mechanisms. Transaction records are constantly updated and can be easily accessed. Distributed Ledger Technology Platforms: A distributed ledger technology (DLT) platform is a secure digital platform that allows data to be securely shared and stored in a decentralized manner across a wide range of nodes within a network. DLT platforms provide a distributed ledger solution that is tamper-resistant, secured using cryptographic encryption protocols, and can remain resilient even in the face of malicious actors. DLT platforms also enable secure and timely data exchanges, providing scalability, reliability and transparency for transactions. Distributed Storage Services: Services that allow users to store data across multiple physical storage locations. This increases reliability and availability of the data and allows for distributed workloads to take advantage of this. Distributed Storage System: A distributed storage system is a series of computer systems which interact together to manage, store, and back up massive amounts of data in a reliable and secure way. A distributed storage system is more fault-tolerant than conventional storage systems because its components are not vulnerable to a single point of failure. This allows a distributed storage system to provide higher levels of data durability and availability than a single, centralized system. Distributed Systems: A type of computing system consisting of multiple, independent components that can communicate with each other to coordinate and synchronize their actions, creating a unified system as a whole. It is a type of software architecture that is designed to maximize resources across multiple computers connected over a network. Distributed Systems Architecture: A distributed system is an interconnection of multiple independent computers that coordinate their activities and share the resources of a network, usually communicating via a message-passing interface or remote procedure calls. Distributed systems architecture includes design guidelines and approaches to ensure a system’s resilience, performance, scalability, availability, and security. Erasure Coded File Storage: Erasure coded file storage is an archive strategy that divides data into portions, and then encodes each portion multiple times using various error-correction algorithms. This redundancy makes erasure coding valuable for permanently storing critical data as it increases reliability but reduces storage costs. Fault Tolerance: Fault tolerance is the capability of a system to continue its normal operation despite the presence of hardware or software errors, disruptions, and even loss of components or data. Fault tolerant systems are designed to be resilient to failure and to continue normal operation in the event of a partial or total system failure. Gossip Protocol: A distributed algorithm in which each member of a distributed system periodically communicates a message to one or more nodes, which then send the same message onto other nodes, until all members of the network receive the same message. This allows for a system to remain aware of all other nodes and records in the system without the need for a central server. Hashgraph: A distributed ledger platform that uses a virtual voting algorithm to achieve consensus faster than a traditional distributed ledger. This system allows for the secure transfer of digital objects and data without needing a third party for authentication. Its consensus algorithm is based on a gossip protocol, where user nodes are able to share news and updates with each other. Hyperledger Fabric: Hyperledger Fabric is an open source software project that provides a foundation for developing applications that use distributed ledgers. It allows for secure and permissioned transactions between multiple businesses, enabling an ecosystem of participants to securely exchange data and assets. It provides support for smart contracts, digital asset management, encryption, and identity services. Immutability: The capacity of an object to remain unchanged over time, even after multiple operations and modifications. Additionally, immutability is a property by which the object remains unchanged, and all operations on that object return a new instance instead of modifying the original. Interoperability: The ability of systems or components to work together and exchange data, even though they may be from different manufacturers and have different technical specifications. Key Management: Key management is the overall management of a set of cryptographic keys used to protect data both in transit over a network and in storage. Proper key management encryption and control ensure the secure communication within applications and systems and provides a baseline for protecting sensitive information. Key managers are responsible for the storage, rotation, and renewal of encryption keys and must ensure that the data is secure against unauthorized disclosure, modification, inclusion, or loss. Merkle Tree: A Merkle tree is a tree-based data structure used for verifying the integrity of large sets of data. It consists of a parent-child relationship of data nodes, with each block in the tree taking the data of all child blocks, hashing it together, and then creating a hash of its own. This process is repeated until a single block remains, which creates a hierarchical and hashed structure. Multi-Node Clusters: A type of computing architecture, composed of multiple connected computers, or nodes, that work together and are able to act as a single system. A multi-node cluster allows for increased information processing and storage capacity, as well as increased reliability and availability. Nodes: A node is a basic unit of a data structure, such as a linked list or tree data structure. Nodes contain data and also may link to other nodes. Nodes are used to implement graphs, linked lists, trees, stacks, and queues. They are also used to represent algorithms and data structures in computer science. Oracles: An oracle is a system designed to provide users with a result of a query. These systems are often relied on to deliver accurate and reliable conclusions to users, based on the data they have provided. There have been several generations of oracles, ranging from hardware to software-based systems, each of which has different sets of capabilities. Orchestration: Orchestration is the process of using software automation to manage and configure cloud-based computing services. It is used to automate the management and deployment of workloads across multiple cloud platforms, allowing organizations to gain efficiency and scalability. Peer-to-Peer Networking: Peer-to-peer networking is a type of network architecture model where computers (or peer nodes) are connected together in such a way that each node can act as a client or a server for the other nodes in the network. In other words, it does not rely on a central server to manage the communication between the connected peers in the network. Proof of Stake (PoS): A consensus mechanism in blockchain technology which allows nodes to validate transactions and produce new blocks according to the amount of coins each node holds. It is an alternative to the Proof of Work (PoW) consensus protocol. In PoS, validators stake their coins, meaning they have to deposit coins with the blockchain protocol before they can validate blocks. Validators receive rewards for creating blocks and are penalized for malicious behavior. Proof of Storage (PoSt): Proof of Storage is a consensus cryptographic mechanism which is used to attest to the storage of data in a distributed storage network. The consensus model requires a randomly chosen subset of storage miners to periodically provide cryptographic proofs that they are storing data correctly and that all necessary nodes are online. The goal of PoSt is to ensure that data stored is secure, as well as increase the security and transparency of distributed storage networks. Proof of Work (PoW): A consensus algorithm by which a set of data called a “block” is validated by solving computationally-taxing mathematical problems. It is used as a security measure in blockchains as each block that is created must have a valid PoW for the block to be accepted. The difficultly of the computational problems get more difficult with time as the blockchain grows, incentivizing the participants to continue to keep the chain running. Proof-of-Stake (PoS): A consensus mechanism used in certain distributed ledger system, where validator nodes participate in block validation with a combination of network participation and staking of token or other resources. The Proof-of-Stake consensus is an alternative to Proof-of-Work (PoW) used in many other blockchain networks. Proof-of-Work: A concept used by blockchain networks to ensure consensus by requiring a certain amount of effort or work to make sure the blocks of data in the chain are legitimate and valid. This is done by large amounts of computational work, typically hashing algorithms. Public Distributed Ledger: A public distributed ledger is a decentralized database that holds information about all the transactions that have taken place across the network, distributed and maintained by all participants without the need for a central authority to manage and validate it. All participants in the network can access, view, and validate the data stored on the ledger. Public Key Infrastructure (PKI): A set of protocols, services, and standards that manage, distribute, identify and authenticate public encryption keys associated with users, computers, and organizations in a network. PKI allows secure communication and ensures that only the intended recipient can read the message. QuickP2P: A type of networking protocol focused on peer-to-peer (P2P) computing that allows users to exchange files and data quickly across multiple computers. QuickP2P works by breaking the files into small blocks, which can then be rapidly downloaded separately in parallel by the searching user. Quorum: The minimum number of nodes in a distributed system that must be engaged in order to reach a consensus. Quorum value is set based on the number of nodes in the system and must be greater than half of the total nodes present in the system. Replication: The process of creating redundant copies of data or objects in distributed systems for the purpose of fault-tolerant data storage or network operations. Routing Protocols: Protocols that direct traffic between computers across a network or the Internet, determining the routing path for data packets and exchanging information about available routes between routers. Routing protocols define the way routers communicate with each other to exchange network information and configure the best path for data traffic. Scalability: The ability of a system to increase its performance or capacity when given additional resources such as additional computing, memory, data storage, network bandwidth and power. Scalability is an important consideration for developing systems that must handle increasing amounts of data, workload or users. Secure Messaging: Secure messaging is the process of sending or receiving encrypted messages between two or more parties, with the intent of ensuring that only the intended recipient can access the contents of the message. The encryption process generally involves the use of public and private keys, making it secure and nearly impossible for anyone else to intercept any messages sent over the network. Secure Multi-Party Computation (MPC): A computer security framework that allows several parties to jointly compute a function over their private inputs without revealing anything other than their respective outputs to the other parties. MPC leverages the security of cryptography in order to achieve privacy and security in computation. Security Protocols: Security protocols are systems of standard rules and regulations created by computer networks to protect data and enable secure communication between devices. They are commonly used for authentication, encryption, confidentiality and data integrity. Smart Contracts: A type of protocol that is self-executing, autonomously carrying out the terms of an agreement between two or more parties when predetermined conditions are met. They are used to exchange money, assets, shares, or anything of value without the need for a third party or intermediary in a secure and trustless manner. Smart contracts are used widely within blockchain-based applications to reduce risk and increase speed and accuracy. Smart Contracts: Contracts written in computer code that are stored and executed on a blockchain network. Smart contracts are self-executing and contain the terms of an agreement between two or more parties that are written directly into the code. Smart contracts are irreversible and are enforced without the need for a third-party. State Machines: A state machine is a system composed of transitioning states, where each state is determined by the previous state and the current inputs. Each state has attached conditions and outputs, and when a the previous state and input conditions match the conditions of the current state, a single output will be generated. State machines are commonly used in computer engineering for finite automation. Synchronization Methods: Methods of ensuring that different parts of a distributed application or system are working from a shared same set of data at a given point in time. This can be achieved by actively sending data among components or by passively waiting for components to ask for data before sending it. Synchronous Byzantine Fault Tolerance (SBFT): A consensus algorithm for blockchain networks that requires each node to be connected and active for all network messages to be exchanged and validated in a given time period. This algorithm provides a way for a distributed network to come to consensus, even when some participants may be compromised or malicious. Time-Stamping: The technique of assigning a unique and precise time value to all events stored in a record in order to define an chronological order of those events. Virtual Voting: Digital voting system where citizens are able to cast their vote over the internet, directly or through a voting portal. It has been used as an alternative to physical voting polls, particularly during the pandemic. It can also be used to verify the accuracy and security of elections. Web 3.0: The Web 3.0 concept refers to the newest generation of the internet. It is highly decentralized and automated, based on AI and distributed ledger technologies such as blockchain. It allows for a more open and secure infrastructure for data storage, authentication, and interactions between devices across the web. It has the potential to be much smarter, faster, and more efficient than its predecessor Web 2.0.\

    .

    CLI

    Get started within minutes

    API

    Instant interaction

    SDKs

    Advanced applications

    Install NodeJS LTS 16.17.1
    shdwDrive CLI
    CLI Guide
    JavaScript
    Rust
    Python
    SDK Guide
    API
    https://github.com/GenesysGo/shdw-drive-bug-reports
    here
    here
    Discord

    Podcasts & Presentations

    Official SHDW and GenesysGo podcast appearances and articles

    The Defiant: The Rise & Fall & Rise of Solana


    Next Billion Podcast: Why Solana is Leading the DePin Game


    Blockworks: Hot take: Your dApp Probably isn’t DePIN


    Crossroads Panel: The Future of DePin


    Crossroads: Discovering the Future of Decentralized Infrastructure






    Guides

    Download shdwDrive:

    Direct Download (APK) | Solana App Store Link (coming soon) | Play Store Link (coming soon)

    Getting Started is Simple

    As a User:

    1. the shdwDrive mobile app

    2. Connect your Solana wallet

    3. Choose a storage plan (start with 5GB free!)

    4. Create your first bucket

    As an Operator:

    As of release 1.0.8, gossip tickets have been refreshed. All current operators must verify their ticket has correctly updated after upgrade/install.

    1. the shdwDrive mobile app

    2. properly

    3. Navigate to the Operator tab

    4. Select your storage level

    As a Developer:

    1. - integrate shdwDrive in your app

    Need specific information? Jump to:

    • - New to shdwDrive? Start here

    • - Learn how to participate in the network

    • - integrate shdwDrive with your app

    • - Dive into the network architecture

    Have questions? Our comprehensive sections below cover everything you need to know about using and operating on shdwDrive v2. Have feedback? Let us know .

    Cover
    Cover
    Cover
    Cover
    Cover
    Cover
    Start uploading your files securely

    Acquire a valid Join Ticket

  • Connect your shdwNode!

  • Economics - Dive deeper into how the protocol works

    Download
    Install
    Adjust settings
    contribution
    Quick Start
    Getting Started Guide
    Becoming an Operator
    Developer Guide
    Technical Details
    FAQ
    here

    Store Files

    Be an Operator

    Join Tickets

    Developers

    Next Billion Podcast: Earn Money from Storage
    GenesysGo x Step Finance
    AI on Solana: Compute, Earn, & Store
    100X Podcast: Decentralizing Big Tech w/ shdwDrive
    GM Solana by JPEG Lord: SHDW Talk
    Solana Documentary
    Next Billion Podcast Episode 50
    Blockworks Article
    Solana Crossroads 2024 Panel
    Crossroads 2024 Feature
    Next Billion Podcast Episode 47
    Step Finance Spaces
    AI on Solana Spaces
    100X Podcast
    GM Solana Podcast

    Phone Settings

    Requirement settings for shdwOperators

    Table of Contents

    1. General Android Power Settings

    2. App Background Behavior

      • (S21 & Newer)

      • (6 & Newer)

      • (10 & Newer)


    General Android Power Settings

    1. Battery Optimization

    1. Go to Settings → Battery (or Battery saver / Battery optimization).

    2. Locate shdwDrive in the list.

    3. Set it to Not optimized or Unrestricted.

    4. This ensures the system does not close the app in the background.

    2. App-Specific Power Controls

    1. Settings → Apps → shdwDrive → Battery (or similar).

    2. Toggle Allow background activity to On.

    3. Disable any “Battery optimization” or “Restrict background data” specifically for shdwDrive.


    App Background Behavior

    1. App Management Settings

    • Never manually force-stop the shdwDrive app.

    • Keep shdwDrive in your Recent Apps (avoid swiping it away).

    • If there’s an Auto-start / Auto-launch feature, turn it on for shdwDrive.

    • Allow it to run in the background continuously.

    2. Memory Management

    • Exclude shdwDrive from any memory cleaners or “one-tap boost” apps.

    • Turn off automatic hibernation for the app.

    • Confirm shdwDrive is on any “Do not optimize” or “White list” for battery/memory.


    Battery Management

    1. Power Maintenance

    • If possible, keep your device plugged in while the node runs.

    • Avoid or disable aggressive power-saving modes that close apps at low battery.

    • Set your low-battery threshold to around 15% so shdwDrive won’t be shut down prematurely.

    • A dedicated power supply can help if you’re running the node for extended periods.

    2. Battery Optimization

    • Disable Adaptive Battery specifically for shdwDrive.

    • Turn off “Optimize for battery life” for shdwDrive.

    • Remove it from any “sleeping apps” or “deep-sleep” lists.


    System-Level Optimization

    1. Memory Settings

    • If your device has Developer Options:

      • Set Background process limit to Standard or No limit.

    • Disable or reduce any “memory optimization” tools that might kill background processes.

    2. Performance Mode

    • Use High performance mode if available for stable connectivity.

    • Reduce or disable thermal throttling on gaming/performance phones if comfortable.

    • Ensure the device has adequate cooling to avoid forced closures.


    Manufacturer-Specific Settings

    Below are recommendations for devices running Android 12L or later. Menu names may vary by region or device.


    Samsung Settings

    Applies to: Galaxy S21, S22, S23, A53, A54, etc. (on Android 12L+)

    1. Battery Settings

      • Settings → Battery → Background usage limits

        • Add shdwDrive to Unrestricted apps.


    Google Pixel Settings

    Applies to: Pixel 6, 6 Pro, 7, 7 Pro, 8, etc. (Android 12L+)

    1. Battery Settings

      • Settings → Battery → Battery Saver

        • Turn it off or only use it manually.


    OnePlus Settings

    Applies to: OnePlus 10, 10T, 11, etc. (Android 12L+)

    1. Battery Optimization

      • Settings → Battery → Advanced → Battery optimization → shdwDrive → Don’t optimize.

    2. Background Process


    Xiaomi Settings

    Applies to: Xiaomi / Redmi / POCO (2022+ devices running 12L+)

    1. Battery & Performance

      • Settings → Battery & performance

      • Turn off Battery saver for shdwDrive or set to Unrestricted.


    OPPO / Realme Settings

    Applies to: OPPO/Realme devices running Android 12L+

    1. Battery Settings

      • Settings → Battery → select High performance mode or exclude from Power saver.

    2. App Management


    ASUS Settings

    Applies to: ZenFone 8/9, ROG Phone 5/6/7 on 12L+

    1. Power Management

      • Settings → Battery → PowerMaster

      • Disable optimization for shdwDrive.

      • Auto-start manager → Allow shdwDrive.


    Motorola Settings

    Applies to: 2022+ models running Android 12L or later

    1. Battery Settings

      • Settings → Battery → Turn off Adaptive Battery for shdwDrive or pick Unrestricted.

    2. App Management


    Nothing Phone Settings

    Applies to: Nothing Phone (1) & (2)

    1. Battery Settings

      • Settings → Battery → Battery optimization → shdwDrive → Don’t optimize.

    2. App Management


    Sony Settings

    Applies to: Xperia devices launched or updated to 12L+

    1. Battery Settings

      • Settings → Battery → turn off Adaptive Battery for shdwDrive.

      • Disable or bypass STAMINA mode if it kills the node.


    Vivo Settings

    Applies to: Vivo (OriginOS/Funtouch OS 12L+)

    1. Battery Settings

      • Settings → Battery → Background power consumption → Allow for shdwDrive.

      • Exclude from power saving mode.

    2. iManager Settings


    Nokia Settings

    Applies to: Nokia models from 2022 onward running 12L+

    1. Power Settings

      • Settings → Battery → Battery optimization → Don’t optimize for shdwDrive.

      • Disable Adaptive Battery for the app if possible.


    TCL Settings

    Applies to: TCL devices on Android 12L+ (2022 releases and newer)

    1. Battery Management

      • Settings → Battery & Performance → App power saving mode → Off for shdwDrive

      • Smart Manager → auto-launch → Enable for shdwDrive

    2. App Settings


    ZTE Settings

    Applies to: ZTE devices on Android 12L+ (e.g., Axon series)

    1. Power Settings

      • Settings → Battery → power saving mode → exclude shdwDrive

      • App power saving → Off for shdwDrive

    2. App Management


    Lenovo Settings

    Applies to: Recent Lenovo tablets / phones with 12L or later

    1. Battery Settings

      • Settings → Battery → Allow background app management for shdwDrive

      • Exclude from power saving modes.

    2. Security Settings


    BlackShark Settings

    Applies to: Black Shark 5 Series and later (Android 12L+)

    1. Game Dock Settings

      • Game Dock → Performance → CPU/GPU → Performance mode

      • Background process → Allow shdwDrive

    2. Battery Settings


    Infinix / Tecno Settings

    Applies to: Infinix/Tecno models running 12L+

    1. Power Management

      • Settings → Battery → Power Management → Off for shdwDrive

      • Background apps → Allow

    2. Phone Master


    Final Tips

    • Check Settings After Updates: System updates can revert your battery or background settings. Review them after each update.

    • Stay Plugged In: A stable power source ensures you won’t lose node connectivity when battery runs low.

    • Monitor Node Activity: If the node goes offline, revisit settings to confirm none have changed.

    By following these instructions on your Android 12L+ device, you’ll help keep shdwDrive running reliably in the background for maximum node uptime. Good luck and happy node-running!

    Cover
    Cover

    Xiaomi / POCO (Newer Models)

  • OPPO / Realme (Android 12L+)

  • ASUS (ZenFone / ROG on 12L+)

  • Motorola (2022+ Models)

  • Nothing Phone (1, 2)

  • Sony Xperia (Recent Models)

  • Vivo (Android 12L+)

  • Nokia (2022+ Models)

  • TCL (12L+ Releases)

  • ZTE (12L+ Releases)

  • Lenovo (Recent Tablets / Phones)

  • Black Shark (5 Series & Newer)

  • Infinix / Tecno (Current 12L+ Models)

  • More battery settings: Disable Adaptive battery and Put unused apps to sleep.
  • App Management

    • Settings → Apps → shdwDrive → Battery → Unrestricted

    • Under Mobile data, enable background data.

    • Keep shdwDrive in memory if such an option is present.

  • Device Care

    • Settings → Device care → Battery → Turn off Adaptive power saving

    • Choose High performance mode if needed.

  • Settings → Apps → shdwDrive → Battery → Unrestricted.
  • Background Process

    • Settings → Apps → Special app access → Background restrictions

    • Make sure shdwDrive is allowed in the background.

  • Memory Management

    • Settings → Developer options → Background process limit → Standard.

  • Settings → Apps → shdwDrive → Battery → Don’t optimize

  • Allow background data usage.

  • System Settings

    • Settings → System settings → RAM boost → Disable if it kills background tasks.

    • Battery → Intelligent Control → Turn off for shdwDrive.

  • App Management
    • Settings → Apps → Manage apps → shdwDrive

    • Autostart → Enable

    • Battery saver → No restrictions

  • Security App (If applicable)

    • In Security → Battery optimization → Disable for shdwDrive.

    • Exclude from memory optimization as well.

  • Settings → Apps → shdwDrive
    • Battery → Allow background activity

    • Startup/Auto-launch → Enable

  • System Settings

    • Settings → Additional Settings → Background app management → Allow shdwDrive.

  • Mobile Manager

    • Mobile Manager → PowerMaster → High performance

    • Keep shdwDrive unrestricted in background.

  • App Specific

    • Settings → Apps → shdwDrive → allow auto-start and background activity.

  • Settings → Apps → shdwDrive → Battery → Unrestricted
  • Mobile data & Wi-Fi → Allow background data.

  • Performance

    • Settings → System → (Developer options or Gestures) → ensure no forced app closures.

  • Settings → Apps → shdwDrive
  • Battery usage → Unrestricted

  • Background process → Allow

  • System Settings

    • Settings → System → Developer options → set Background process limit to Standard or no limit.

  • App Management
    • Settings → Apps → shdwDrive → Advanced → Battery optimization → Don’t optimize.

    • Allow background data usage.

  • Power Management

    • Check that Adaptive battery is off or not affecting shdwDrive.

    • Let the app run freely in background settings.

  • iManager → App Manager → Auto-start → Enable for shdwDrive.

  • Background running → Allow.

  • App Management

    • Settings → Apps → shdwDrive → Background wake up → Allow

    • Background running permission → Allow

  • App Management
    • Settings → Apps & notifications → shdwDrive → Advanced → Background restrictions → Off

    • Battery → Unrestricted

  • Background Activity

    • Settings → System → Developer options → Background process limit → Standard

  • Settings → Apps → shdwDrive → Battery → Don’t restrict

  • Background process → Allow

  • System Optimization

    • Settings → Privacy → Smart Manager → Battery optimization → Disable for shdwDrive

  • Settings → Apps → shdwDrive → Battery → Unrestricted

  • Auto-start → Enable

  • System Settings

    • Settings → Power Manager → Battery optimization → Don’t optimize for shdwDrive

  • Security Center → App management → Auto-start → enable for shdwDrive

  • Background apps → allow

  • App Specific

    • Settings → Apps → shdwDrive → Battery → Don’t optimize

    • Background mobile data → Allow

  • Settings → Battery → App battery saver → disable for shdwDrive

  • Performance mode → On when plugged in

  • System Settings

    • Settings → Additional settings → Developer options → Background process → Standard

  • Phone Master → Auto-start → Enable shdwDrive

  • Background running → Allow

  • App Management

    • Settings → Apps → shdwDrive → Power usage → Don’t restrict

    • Auto-launch → Enable

  • Battery Management
    System-Level Optimization
    Manufacturer-Specific Settings
    Samsung
    Google Pixel
    OnePlus
    Final Tips
    Cover

    FAQ

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

    Technical

    Where can I go to reach out for technical assistance?

    Our is the best place to get in touch with us. We have a dedicated support section.

    Discord Server:

    I'm getting a "410 Gone" error when trying to use the CLI. What should I do?

    This error means the Solana RPC provider you're using with the CLI doesn't support a specific RPC method that's necessary for the CLI to function. This could be `getProgramAccounts` or some other method.

    We'd recommend trying a more premium RPC provider like , Hellomoon.io, or some other premium Solana RPC provider that allows for all Solana RPC methods to be used.

    What should I do if creating a storage account is failing?

    If creating a storage account is failing, make sure that you have appropriate amounts of both SOL and SHDW in your wallet. Creating a storage account requires a small amount of SOL to cover the transaction fee, as well as some SHDW to cover the initial storage allocation. Make sure that your wallet has enough funds to cover these requirements. Review the docs here: https://docs.shadow.cloud/build/the-cli#create-a-storage-account

    If you have the correct amount of SOL and SHDW in your wallet but creating a storage account is still failing, there may be other factors at play that are causing the issue. Some possible causes could be network connectivity issues, problems with the ShdwDrive node, or bugs/issues with the SDK.

    To troubleshoot the issue, you can try the following:

    How much storage space can I reserve?

    A user can reserve 4kb at minimum.

    There is an upper limit of one terabyte (1TB) per bucket.

    Development is currently underway which will greatly increase this cap.

    What's the smallest or largest file I can upload?

    Currently, these are the limits:

    • Minimum: 4kb. If you upload a 100 byte file, it will still take up 4kb of space. This is due to the replication overhead required.

    • Maximum: 1gb.

    What should I do if I think there is an issue with how I'm implementing the wallet, or my transactions are not working?

    If you think there is an issue with how you're implementing the wallet, or your transactions are not working, you can try upgrading the wallet adapters. Check the Solana wallet adapter repositories for their examples, as the process for importing the adapters may have changed.

    Additionally, you can refer to the ShdwDrive documentation and SDK for more information on how to properly implement the wallet and perform transactions. You can review the example here: https://docs.shadow.cloud/build/the-sdk/sdk-javascript#example-post-request-via-sdk-make-immutable

    If you are using react to build a wallet using const drive = await new ShdwDrive(connection, wallet).init(); and getting the error "Cannot read properties of undefined (reading 'toBytes')" then remember to make sure you must pass the entire wallet around and make sure to not deconstruct it.

    I can create a storage account using the phantom wallet through CLI, but when I try from the SDK in my app the transaction fails saying insufficient balance. Why is this?

    For the purposes of utilizing the ShdwDrive, ~0.1 SOL in our experience will avoid insufficient balance errors. You can also examine the TXs to see if there's any differences in your spend when using the CLI versus the SDK methods.

    Does shdwDrive support Ledger wallet signing?

    No, ShdwDrive does not currently support Ledger wallet signing. The reason we are currently unable to provide Ledger support is due to the absence of the message signing feature in the Solana app for Ledger, as our system relies on this functionality.

    To expedite the implementation of Ledger support, kindly consider drawing attention to this GitHub issue by leaving a comment: https://github.com/solana-labs/wallet-adapter/pull/712

    Are accounts returned in any specific order when calling the `getStorageAccounts` method?

    Yes, accounts are returned in the order they are created when calling the getStorageAccounts method in GenesysGo ShdwDrive. This is because the system was designed and built in such a way to ensure that the accounts are returned in the order they were created. https://docs.shadow.cloud/build/the-sdk/sdk-javascript#getstorageaccounts

    Is there a way to delete multiple files at the same time?

    Currently, it is not possible to delete multiple files at once. However, we have added this feature to our roadmap and will be working on it in the near future. Thank you for your suggestion!

    Is there a reason why `edit-file` works differently from `upload-file` when generating upload hashes server side?

    The edit-file functionality works differently from upload-file because it is a remnant of the first iteration of ShdwDrive where every file had an associated account on-chain with some metadata that was crucial for tracking. However, we've made some changes that aren't documented yet and aren't implemented in the SDKs. If you add overwrite: true to the request body of an upload request that you make manually instead of through the SDK, it will do the same thing as editing a file.

    Is it possible to ask the user to sign a shdw transaction and another transaction at the same time on the frontend?

    Currently, it is not possible to ask the user to sign a Shdw transaction and another transaction at the same time on the frontend. The Shdw network only allows ShdwDrive-specific transactions to have instructions related to the ShdwDrive on chain program. Any other instructions will cause the transaction to fail. This security feature is in place to prevent malicious transactions.

    I'm trying to create a File object on my React app to upload it to shdw but I keep getting an error.

    The error you're getting may be due to the ShdwDrive instance being created before the wallet-provider is ready. In the latest example on the main branch, there is a slight change in the useEffect that creates the drive instance which may resolve your issue. Additionally, make sure that the file data buffer is converted to a Blob using new Blob([Buffer.from("data")]).

    I'm using `createStorageAccount` on a node script and it works fine, but when I try to use it in my React app, I get a 403 error.

    By default, the rpc used is the Solana mainnet rpc api.mainnet-beta.solana.com. If you're getting blocked by that, you'll have to sign up for a paid RPC as we cannot control how the Solana mainnet rpc endpoint is limited. It is possible that the endpoint is blocking requests from the browser due to security reasons.

    For additional help, consider joining our and asking in support channels.

    Why does my method fail with "Blockhash not found" error?

    This is an issue on the Solana RPC side and unfortunately, all you can do is retry the method. Consider implementing retry and/or error handling in your application.

    How would I use the SDK to get file contents from the account?

    You can send a normal GET request to https://shdw-drive.genesysgo.net// to get the file contents from the account. You can read more in API methods here: https://docs.shadow.cloud/build/the-api

    Is there a way to get information about filetypes so I can then handle different types?

    You can make a HEAD request or a GET request to get information about file types. If you make a GET request, the response headers should include the content type. Review the API methods here: https://docs.shadow.cloud/build/the-api

    How can I get metadata for the file?

    You can get metadata for the file by making a POST request to https://shdw-drive.genesysgo.net//. The response will include metadata for the file. Review the API methods here: https://docs.shadow.cloud/build/the-api

    Is it possible to transfer ownership of a storage account to another wallet?

    Currently, this is not an active feature in the CLI or SDK. However, it is a planned feature for future releases.

    Can only the owner of the storage edit files?

    Yes, currently only the owner of the storage account can edit the files.

    Is it possible to resume an upload from where it left off if it fails?

    No, unfortunately it is not possible to resume an upload from where it left off if it fails. However, the CLI checks files before uploading and skips them if they already exist. You also receive an output JSON file for each file upload, which will indicate if a file already exists.

    Can I use the rust SDK in anchor programs?

    No, the SDK requires internet access to send http requests. This is not allowed within Solana runtime because arbitrary http responses are not deterministic and may produce different Solana ledger state transitions

    Is it possible for a user to sign a shdw transaction and another unrelated transaction at the same time?

    Currently, the Shdw network only allows ShdwDrive-specific transactions to include instructions related to the ShdwDrive on-chain program. Any other instructions will cause the transaction to fail as a security measure. This means that it is not possible for a user to sign a Shdw transaction and another unrelated transaction at the same time.

    I'm getting a 400 error.

    When getting 400 timeouts for transaction submissions, it is most likely due to congestion on the Solana network. While timing out and retrying is normal during Solana congestion, many are now using priority fees which may help solve congestion-related issues. Contact your RPC provider for further help.

    If your 400 error is stating "Invalid transaction supplied" then you may need to join our support channel in and provide more details on the specific method. To resolve the typical causes of this error do the following:

    1. Check announcements in Discord (https://discord.gg/genesysgo) or the network status (https://status.genesysgo.net/) to make sure there is no platform-wide problem.

    What should I do if I encounter an ENOTFOUND error when using the shdwDrive CLI?

    If you encounter an ENOTFOUND error when using the ShdwDrive CLI, it is likely a local DNS issue on your side. ENOTFOUND is a DNS resolver problem, which means you will need to check with your Internet Service Provider (ISP) to resolve the issue. Alternatively, you can try using a Virtual Private Network (VPN) to see if that resolves the issue.

    What are some things I should check when getting errors?

    You can try setting --log-level debug with your command that is getting an error. Make sure to confirm you have installed the latest versions and dependencies and that your keypair file is being accessed properly. Make sure you wallet is funded properly with both SOL and SHDW, that you are handling Solana connection objects properly, and that you are not having Solana RPC related errors. For further help you can capture logs and share relevant code in the technical support channels of our .

    What does "Internal Server Error" mean when calling the shdwDrive API?

    There are a few reasons for this error but the most common is the file that have not migrated from the original version 1 format storage account to the newer version 2 format. For users that have created legacy style ShdwDrive accounts, please finish the migration steps.

    For additional help please reach out to to us in Discord (https://discord.gg/genesysgo).

    How do I submit a bug or a security issue?

    https://github.com/GenesysGo/shdw-drive-bug-reports

    We adhere to a responsible disclosure process for security related issues. To ensure the responsible disclosure and handling of security vulnerabilities, we ask that you follow the process outlined below.

    Bug Reporting Process

    1. Submit a new bug report by creating a in this repository. https://github.com/GenesysGo/shdw-drive-bug-reports/issues/new/choose

    Is there a way to monitor the network so I know if there are issues or downtime?

    Yes, you can subscribe to the Shdw Network status here: https://status.genesysgo.net/

    Also follow us on twitter https://twitter.com/GenesysGo or join our tech support Discord: https://discord.gg/genesysgo

    How can I add more to my storage account for mutable fees?
    1. Either use the `topUp` method in one of the or send $SHDW directly to the storage account's token address

    2. Use the `refreshStake` method in one of the SDKs to refresh your storage account's stake status. This is not done for you, you must do this step manually.

    How much are the Mutable storage fees?

    Mutable storage fees target a specific USD price. Currently, that is $0.05 USD per gibibyte per year. This comes out to $0.0002739726 USD per gib per Solana Epoch (interval for which mutable storage fees are collected. This price target is converted to $SHDW/$USDC at the time of fee collection.

    Mutable storage fees are collected for bytes stored.

    Where should I send PRs if I have a good example to share? Is there a way to give feedback on the technical documents?

    We welcome any feedback and examples you can provide to our documentation. You can submit a PR to our technical documents repository here - https://github.com/GenesysGo/docs-Shdw-cloud/tree/main - and we will find a good place for it.

    General

    What makes shdwDrive unique?

    ShdwDrive is a commodity cloud network that offers multiple service options, leveraging distributed ledger technology, and offering vertically integrated, L1-specific storage and compute. It is the only cloud network designed to democratize the earnings of traditional cloud platforms without sacrificing performance. Being S3-compatible, ShdwDrive maintains an open-source SDK and interoperability standards that make it easy to access through popular builder tools and SDKs. Its objective is to support popular tools that make building easier, regardless of the application you are building.

    How does shdwDrive ensure data privacy and security?

    ShdwDrive ensures data privacy and security by encrypting and erasure coding the data, and then algorithmically distributing the fragments across the distributed network. This is done trustlessly via smart contracts and requires signed Solana transactions, creating a publicly verifiable on-chain log. Additionally, ShdwDrive provides developers with the tools they need to comply with GDPR and can show records that prove that they have deleted a user's personal data.

    How is GDPR handled?

    ShdwDrive provides developers with tools to comply with GDPR and can provide records to prove the deletion of a user's personal data. All records for GDPR compliance are stored on-chain and have been verified by the Solana validator network. The data is then encrypted and algorithmically distributed across the network in triplicate. All transactions are signed and publicly verifiable on-chain.

    Is shdwDrive supported on mobile?

    Yes, ShdwDrive is supported on mobile through our ecosystem partners who are actively building on mobile. Please check out our Shdw Ecosystem page for more details. https://docs.shadow.cloud/build/community-mainted-uis

    Additionally, in the future, our D.A.G.G.E.R. distributed ledger technology will enable Solana Saga powered storage solutions for those seeking low cost decentralized mobile clouds. Please check out the Learn section for more information. You can read more here: https://docs.shadow.cloud/learn#compute

    Is shdwDrive S3-compatible?

    Yes, ShdwDrive is S3-compatible. S3-compatibility is a widely adopted standard in the cloud storage industry, and many providers offer S3-compatible APIs and protocols, which gives builders greater flexibility in choosing a cloud storage provider. This means developers can easily move data between different services without worrying about compatibility issues. Additionally, S3-compatibility offers robust APIs that enable fast and reliable query, along with virtual mount capability, making it important for Web2, Web3, and the frontiers of distributed ledger tech and AI. ShdwDrive aims to empower developers to integrate it directly into their builds, and to support the talented community of designers who will create innovative platforms for ShdwDrive. You can read more here: https://docs.shadow.cloud/learn/design#s3-compatibility

    What physical infrastructure powers shdwDrive?

    ShdwDrive runs on a global network of bare metal infrastructure, with all compute and storage existing on bare metal. There is no dependency on cloud providers for ShdwDrive operations. For more details on the design of ShdwDrive, please see the "Design" section under the "Learn" category: https://docs.shadow.cloud/learn/design

    What is GenesysGo?

    GenesysGo (GG) is a company that was founded in April 2021 as a Solana validator. Since then, GG has expanded its offerings to focus on a large ecosystem of tools and infrastructure for Solana. More details about the scope of our offerings can be found under the "Learn" category. GG has a team of talented developers and coders who are dedicated to building innovative solutions for the Solana community. For more information, you can visit our website at https://shdwdrive.com.

    Verify that the ShdwDrive network is up and running. https://status.genesysgo.net/

  • Check the ShdwDrive Change Log for any known issues or bugs that may be causing the problem. https://docs.shadow.cloud/reference/change-logs

  • Contact ShdwDrive support for further assistance. https://discord.gg/genesysgo

  • With the s3-compatible client access, you're able to upload files up to 1TiB.

    There's ongoing development to increase the maximum file size.

    If you're still having issues, contact ShdwDrive support for further assistance.

    Check all of your versions and dependencies. You Solana wallet adapter dependencies and the version of the JavaScript SDK must be up to date.

  • Double check the wallet you have chosen to work with is not having issues. You may need to reach out to them directly.

  • Please provide a clear and concise description of the issue, steps to reproduce it, and any relevant screenshots or logs.

  • Label your issue as a 'bug' or 'security' accordingly.

  • Important: For security-related issues, do not include sensitive information in the issue description. Instead, submit a pull request to our repository, containing the necessary details, so that the information remains concealed until the issue is resolved.

    Security related issues should only be reported through this repository.

    Discord server
    https://discord.com/invite/shdwdrive
    Helius
    Discord
    Discord
    Discord
    new issue
    $SHDW
    SDKs
    Cover
    Cover
    Cover

    API

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

    Contents

    storage-account

    POST https://shadow-storage.genesysgo.net

    Creates a new storage account

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    storage-account-info

    POST https://shadow-storage.genesysgo.net

    Gets on-chain and ShdwDrive Network data about a storage account

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    upload

    POST https://shadow-storage.genesysgo.net

    Uploads a single file or multiple files at once Request content type: multipart/form-data

    Parameters (FormData fields)

    Request Body

    Name
    Type
    Description

    edit

    POST https://shadow-storage.genesysgo.net

    Edits an existing file

    Request content type: multipart/form-data

    Parameters (FormData fields)

    Request Body

    Name
    Type
    Description

    list-objects

    POST https://shadow-storage.genesysgo.net

    Get a list of all files associated with a storage account

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    list-objects-and-sizes

    POST https://shadow-storage.genesysgo.net

    Get a list of all files and their size associated with a storage account

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    get-object-data

    POST https://shadow-storage.genesysgo.net

    Get information about an object

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    delete-file

    POST https://shadow-storage.genesysgo.net

    Deletes a file from a given Storage Account

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    add-storage

    POST https://shadow-storage.genesysgo.net

    Adds storage

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    reduce-storage (updated)

    POST https://shadow-storage.genesysgo.net

    Reduces storage

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    make-immutable (updated)

    POST https://shadow-storage.genesysgo.net

    Makes file immutable

    Request content type: application/json

    Request Body

    Name
    Type
    Description

    Example - Secure Sign and Upload File to ShdwDrive using API

    This example demonstrates how to securely upload files to the ShdwDrive using the provided API. It includes the process of hashing file names, creating a signed message, and sending the files along with the necessary information to the ShdwDrive endpoint.

    Example - Editing a File in ShdwDrive using API and Message Signature Verification

    In this example, we demonstrate how to edit a file in ShdwDrive using the API and message signature verification. The code imports necessary libraries, constructs a message to be signed, encodes and signs the message, and sends an API request to edit the file on ShdwDrive.

    Example - Deleting a File from ShdwDrive using Signed Message and API

    In this example, we demonstrate how to delete a file from the ShdwDrive using a signed message and the ShdwDrive API. The code first constructs a message containing the storage account and the file URL to be deleted. It then encodes and signs the message using the tweetnacl library. The signed message is then converted to a bs58-encoded string. Finally, a POST request is sent to the ShdwDrive API endpoint to delete the file.

    CLI

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new for instructions.

    Contents

    url*

    String

    Url of the original file you want to edit. Example:

    https://shdw-drive.genesysgo.net/<storage-account>/<file-name>

    transaction*

    Serialized create storage account transaction that's partially signed by the storage account owner

    storage_account*

    String

    Publickey of the storage account you want to get information for

    file*

    The file you want to upload. You may add up to 5 files each with a field name of

    file

    .

    message*

    String

    Base58 message signature.

    signer*

    String

    Publickey of the signer of the message signature and owner of the storage account

    storage_account*

    String

    Key of the storage account you want to upload to

    file*

    String

    The file you want to upload. You may add up to 5 files each with a field name of

    file

    .

    message*

    String

    Base58 message signature.

    signer*

    String

    Publickey of the signer of the message signature and owner of the storage account

    storage_account*

    String

    Key of the storage account you want to upload to

    storageAccount

    String

    String version of the storage account PublicKey that you want to get a list of files for

    storageAccount*

    String

    String version of the storage account PublicKey that you want to get a list of files for

    location*

    String

    URL of the file you want to get information for

    message

    String

    Base58 message signature.

    signer

    String

    Publickey of the signer of the message signature and owner of the storage account

    location

    String

    URL of the file you want to delete

    transaction *

    String

    Serialized add storage transaction that is partially signed by the ShdwDrive network

    transaction *

    String

    Serialized reduce storage transaction that is partially signed by the ShdwDrive network

    transaction

    String

    Serialized make immutable transaction that is partially signed by the ShdwDrive network

    Example - Sign and upload a file
    Example - Edit a file
    Example - Delete a file
    Example Implementation
    Example Implementation
    Example Implementation
    {
        "shdw_bucket": String,
        "transaction_signature": String
    }
    {
      storage_account: PublicKey,
      reserved_bytes: Number,
      current_usage: Number,
      immutable: Boolean,
      to_be_deleted: Boolean,
      delete_request_epoch: Number,
      owner1: PublicKey,
      owner2: PublicKey,
      accountCoutnerSeed: Number,
      creation_time: Number,
      creation_epoch: Number,
      last_fee_epoch: Number,
      identifier: String
      version: "V1"
    }
    {
      storage_account: PublicKey,
      reserved_bytes: Number,
      current_usage: Number,
      immutable: Boolean,
      to_be_deleted: Boolean,
      delete_request_epoch: Number,
      owner1: PublicKey,
      accountCoutnerSeed: Number,
      creation_time: Number,
      creation_epoch: Number,
      last_fee_epoch: Number,
      identifier: String,
      version: "V2"
    }json
    {
        "finalized_locations": [String],
        "message": String
        "upload_errors": [{file: String, storage_account: String, error: String}] or [] if no errors
    }
    {
        "finalized_location": String,
        "error": String or not provided if no error
    }
    {
        "keys": [String]
    }
    {
        "files": [{"file_name": String, size: Number}]
    }
    JSON object of the file's metadata in the ShdwDrive Network or an error
    {
        "message": String,
        "error": String or not passed if no error
    }
    {
        message: String,
        transaction_signature: String,
        error: String or not provided if no error
    }
    {
        message: String,
        transaction_signature: String,
        error: String or not provided if no error
    }
    {
        message: String,
        transaction_signature: String,
        error: String or not provided if no error
    }
    import bs58 from 'bs58'
    import nacl from 'tweetnacl'
    import crypto from 'crypto'
    
    // `files` is an array of each file passed in.
    const allFileNames = files.map(file => file.fileName)
    const hashSum = crypto.createHash("sha256")
    // `allFileNames.toString()` creates a comma-separated list of all the file names.
    const hashedFileNames = hashSum.update(allFileNames.toString())
    const fileNamesHashed = hashSum.digest("hex")
    // `storageAccount` is the string representation of a storage account pubkey
    let msg = `Shadow Drive Signed Message:\nStorage Account: ${storageAccount}\nUpload files with hash: ${fileNamesHashed}`;
    const fd = new FormData();
    // `files` is an array of each file passed in
    for (let j = 0; j < files.length; j++) {
        fd.append("file", files[j].data, {
            contentType: files[j].contentType as string,
            filename: files[j].fileName,
        });
    }
    // Expect the final message string to look something like this if you were to output it
    // ShdwDrive Signed Message:
    // Storage Acount: ABC123
    // Upload files with hash: hash1
    
    // If the message is not formatted like above exactly, it will fail message signature verification
    // on the ShdwDrive Network side.
    const encodedMessage = new TextEncoder().encode(message);
    // Uses https://github.com/dchest/tweetnacl-js to sign the message. If it's not signed in the same manor,
    // the message will fail signature verification on the ShdwNetwork side.
    // This will return a base58 byte array of the signature.
    const signedMessage = nacl.sign.detached(encodedMessage, keypair.secretKey);
    // Convert the byte array to a bs58-encoded string
    const signature = bs58.encode(signedMessage)
    fd.append("message", signature);
    fd.append("signer", keypair.publicKey.toString());
    fd.append("storage_account", storageAccount.toString());
    fd.append("fileNames", allFileNames.toString());
    const request = await fetch(`${SHDW_DRIVE_ENDPOINT}/upload`, {
        method: "POST",
        body: fd,
    });
    import bs58 from 'bs58'
    import nacl from 'tweetnacl'
    
    // `storageAccount` is the string representation of a storage account pubkey
    // `fileName` is the name of the file to be edited
    // `sha256Hash` is the sha256 hash of the new file's contents
    const message = `ShdwDrive Signed Message:\n StorageAccount: ${storageAccount}\nFile to edit: ${fileName}\nNew file hash: ${sha256Hash}`
    // Expect the final message string to look something like this if you were to output it
    // ShdwDrive Signed Message:
    // Storage Acount: ABC123
    // File to delete: https://shadow-drive.genesysgo.net/ABC123/file.png
    
    // If the message is not formatted like above exactly, it will fail message signature verification
    // on the ShdwDrive Network side.
    const encodedMessage = new TextEncoder().encode(message);
    // Uses https://github.com/dchest/tweetnacl-js to sign the message. If it's not signed in the same manor,
    // the message will fail signature verification on the Shdw Network side.
    // This will return a base58 byte array of the signature.
    const signedMessage = nacl.sign.detached(encodedMessage, keypair.secretKey);
    // Convert the byte array to a bs58-encoded string
    const signature = bs58.encode(signedMessage)
    
    
    const fd = new FormData();
    fd.append("file", fileData, {
        contentType: fileContentType as string,
        filename: fileName,
    });
    fd.append("signer", keypair.publicKey.toString())
    fd.append("message", signature)
    fd.append("storage_account", storageAccount)
    
    const uploadResponse = await fetch(`${SHDW_DRIVE_ENDPOINT}/edit`, {
        method: "POST",
        body: fd,
    });
    import bs58 from 'bs58'
    import nacl from 'tweetnacl'
    
    // `storageAccount` is the string representation of a storage account pubkey
    // `url` is the link to the ShdwDrive file, just like the previous implementation needed the url input
    const message = `ShdwDrive Signed Message:\nStorageAccount: ${storageAccount}\nFile to delete: ${url}`
    // Expect the final message string to look something like this if you were to output it
    // ShdwDrive Signed Message:
    // Storage Acount: ABC123
    // File to delete: https://shadow-drive.genesysgo.net/ABC123/file.png
    
    // If the message is not formatted like above exactly, it will fail message signature verification
    // on the ShdwDrive Network side.
    const encodedMessage = new TextEncoder().encode(message);
    // Uses https://github.com/dchest/tweetnacl-js to sign the message. If it's not signed in the same manor,
    // the message will fail signature verification on the Shdw Network side.
    // This will return a base58 byte array of the signature.
    const signedMessage = nacl.sign.detached(encodedMessage, keypair.secretKey);
    // Convert the byte array to a bs58-encoded string
    const signature = bs58.encode(signedMessage)
    const deleteRequestBody = {
        signer: keypair.publicKey.toString(),
        message: signature,
        location: options.url
    }
    const deleteRequest = await fetch(`${SHDW_DRIVE_ENDPOINT}/delete-file`, {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(deleteRequestBody)
    })

    Install shdwDrive CLI

    • Video Walkthrough

    • Install Solana CLI

    • Create a Storage Account

  • Install Rust CLI - Experimental!

  • Introduction

    The CLI is the easiest way to interact with shdwDrive. You can use your favorite shell scripting language, or just type the commands one at a time. For test driving shdwDrive, this is the best way to get started.

    Install the shdwDrive CLI

    Prerequisites: Install NodeJS LTS 16.17.1 on any OS.

    Then run the following command

    Video Guide and Walkthrough

    Install the Solana CLI

    In order to interact with shdwDrive, we're going to need a Solana wallet and CLI to interact with the Solana blockchain.

    NOTE: The shdwDrive CLI uses it's own RPC configuration. It does not use your Solana environment configuration.

    Check HERE for the latest version.

    Upon install, follow that up immediately with:

    Create a Keypair file

    We need to have a keypair in .json format to use the shdwDrive CLI. This is going to be the wallet that owns the storage account. If you want, you can convert your browser wallet into a .json file by exporting the private keys. Solflare by default exports it in a .json format (it looks like a standard array of integers, [1,2,3,4...]. Phantom, however, needs some help and we have just the tool to do that.

    If you want to create a new wallet, just use

    You will see it write a new keypair file and it was display the pubkey which is your wallet address.

    You'll need to send a small amount of SOL and SHDW to that wallet address to proceed! The SOL is used to pay for transaction fees, the SHDW is used to create (and expand) the storage account!

    Context-Sensitive Help

    shdwDrive CLI comes with integrated help. All shdwDrive commands begin with shdw-drive.

    The above command will yield the following output

    You can get further help on each of these commands by typing the full command, followed by the --help option.

    Create a Storage Account

    This is one of the few commands where you will need SHDW. Before the command executes, it will prompt you as to how much SHDW will be required to reserve the storage account. There are three required options:

    -kp, --keypair

    • Path to wallet that will create the storage account

    -n, --name

    • What you want your storage account to be named. (Does not have to be unique)

    -s, --size

    • Amount of storage you are requesting to create. This should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported.

    Example:

    Upload File to shdwDrive

    Options for this command:

    -kp, --keypair

    • Path to wallet that will upload the file

    -f, --file

    • File path. Current file size limit is 1GB through the CLI.

    If you have multiple storage accounts it will present you with a list of owned storage accounts to choose from. You can optionally provide your storage account address with:

    -s, --storage-account

    • Storage account to upload file to.

    --rpc <your-RPC-endpoint>

    • RPC endpoint to pass custom endpoint. This can resolve 410 errors if you are using methods not available from the default free public endpoint.

    Example 1:

    Example 2 with RPC:

    Upload Multiple Files to shdwDrive

    A more realistic use case is to upload an entire directory of, say, NFT images and metadata. It's basically the same thing, except we point the command to a directory.

    Options:

    -kp, --keypair

    • Path to wallet that will upload the files

    -d, --directory

    • Path to folder of files you want to upload.

    -s, --storage-account

    • Storage account to upload file to.

    -c, --concurrent

    • Number of concurrent batch uploads. (default: "3")

    --rpc <your-RPC-endpoint>

    • RPC endpoint to pass custom endpoint. This can resolve 410 errors if you are using methods not available from the default free public endpoint.

    Example 1:

    Example 2 with RPC:

    Edit a File (aka Overwrite a File aka Replace a File)

    This command is used to replace an existing file that has the exact same name. If you attempt to upload this file using edit-file and an existing file with the same name is not already there, the request will fail.

    There are three requirements for this command:

    -kp, --keypair

    • Path to wallet that will upload the file

    -f, --file

    • File path. Current file size limit is 1GB through the CLI. File must be named the same as the one you originally uploaded

    -u, --url

    • ShdwDrive URL of the file you are requesting to delete

    Example:

    Delete a File

    This is straightforward and it's important to note once it's deleted, it's gone for good.

    There are two requirements and there aren't any options outside of the standard ones:

    -kp, --keypair

    • Path to the keypair file for the wallet that owns the storage account and file

    -u, --url ShdwDrive URL of the file you are requesting to delete

    Example:

    Add Storage to Storage Account

    You can expand the storage size of a storage account. This command consumes SHDW tokens.

    There are only two requirements for this call

    -kp, --keypair

    • Path to wallet that will upload the files

    -s, --size

    • Amount of storage you are requesting to add to your storage account. Should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported currently

    If you have more than one account, you'll get to pick which storage account you want to add storage to.

    Example:

    Reduce Storage Account Size

    You can reduce your storage account and reclaim your unused SHDW tokens. This is a two part operation where you first reduce your account size, and then request your SHDW tokens. First, let's reduce the storage account size.

    There are two requirements

    -kp, --keypair

    • Path to wallet that will upload the files

    -s, --size

    • Amount of storage you are requesting to remove from your storage account. Should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported currently

    Example:

    Claim Stake (aka Claim Unused SHDW Tokens after Reduction)

    Since you reduced the amount of storage being used in the previous step, you are now free to claim your unused SHDW tokens. The only requirement here is a keypair.

    Example:**

    Delete a Storage Account

    You can entirely remove a storage account from ShdwDrive. Upon completion, your SHDW tokens will be returned to the wallet.

    NOTE: You have a grace period upon deletion that lasts until the end of the current Solana epoch. Go HERE to see how much time is remaining in the current Solana epoch to know how much grace period you will get.

    All you need here is a keypair, and it will prompt you for the specific storage account to delete.

    Example:

    Undelete a Storage Account

    Assuming the epoch is still active, you can undelete your storage account. You only need a keypair. You will be prompted to select a storage account when running the command. This removes the deletion request.

    Make Storage Account Immutable

    One of the most unique and useful features of ShdwDrive is that you can make your storage truly permanent. With immutable storage, no file that was uploaded to the account can ever be deleted or edited. They are solidified and permanent, as is the storage account itself. You can still continue to upload files to an immutable account, as well as add storage to an immutable account.

    The only requirement is a keypair. You will be prompted to select a storage account when running the command.

    Example:

    The Rust CLI

    (This section is under development)

    CreateStorageAccount

    Create an account on which to store data. Storage accounts can be globally, irreversibly marked immutable for a one-time fee. Otherwise, files can be added or deleted from them, and space rented indefinitely.

    Parameters:

    --name

    • String

    --size

    • Byte

    Example:

    DeleteStorageAccount

    Queues a storage account for deletion. While the request is still enqueued and not yet carried out, a cancellation can be made (see cancel-delete-storage-account subcommand).

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    CancelDeleteStorageAccount

    Cancels the deletion of a storage account enqueued for deletion.

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    ClaimStake

    Redeem tokens afforded to a storage account after reducing storage capacity.

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    AddStorage

    Increase the capacity of a storage account.

    Parameters:

    --storage-account

    • Pubkey

    --size

    • Byte (accepts KB, MB, GB)

    Example:

    Example:

    AddImmutableStorage

    Increase the immutable storage capacity of a storage account.

    Parameters:

    --storage-account

    • Pubkey

    --size

    • Byte (accepts KB, MB, GB)

    Example:

    Example:

    ReduceStorage

    Reduce the capacity of a storage account.

    Parameters:

    --storage-account

    • Pubkey

    --size

    • Byte (accepts KB, MB, GB)

    Example:

    Example:

    MakeStorageImmutable

    Make a storage account immutable. This is irreversible.

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    GetStorageAccount

    Fetch the metadata pertaining to a storage account.

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    GetStorageAccounts

    Fetch a list of storage accounts owned by a particular pubkey. If no owner is provided, the configured signer is used.

    Parameters:

    --owner

    • Option<Pubkey>

    Example:

    Example:

    ListFiles

    List all the files in a storage account.

    Parameters:

    --storage-account

    • Pubkey

    Example:

    Example:

    GetText

    Get a file, assume it's text, and print it.

    Parameters:

    --storage-account

    • Pubkey

    --filename

    Example:

    Example:

    GetObjectData

    Get basic file object data from a storage account file.

    Parameters:

    --storage-account

    • Pubkey

    --file

    • String

    Example:

    Example:

    DeleteFile

    Delete a file from a storage account.

    Parameters:

    --storage-account

    • Pubkey

    --filename

    • String

    Example:

    Example:

    EditFile

    Edit a file in a storage account.

    Parameters:

    --storage-account

    • Pubkey

    --path

    • PathBuf

    Example:

    Example:

    StoreFiles

    Upload one or more files to a storage account.

    Parameters:

    --batch-size

    • usize (default: value of FILE_UPLOAD_BATCH_SIZE)

    --storage-account

    • Pubkey

    --files

    • Vec<PathBuf>

    Example:

    Example:

    developer guide
    npm install -g @shadow-drive/cli
    sh -c "$(curl -sSfL https://release.solana.com/v1.14.3/install)"
    export PATH="/home/sol/.local/share/solana/install/active_release/bin:$PATH"
    solana-keygen new -o ~/shdw-keypair.json
    shdw-drive help
    shdw-drive create-storage-account --help
    shdw-drive create-storage-account -kp ~/shdw-keypair.json -n "pony storage drive" -s 1GB
    shdw-drive upload-file -kp ~/shdw-keypair.json -f ~/AccountHolders.csv
    shdw-drive upload-file -kp ~/shdw-keypair.json -f ~/AccountHolders.csv --rpc <https://some-solana-api.com>
    shdw-drive upload-multiple-files -kp ~/shdw-keypair.json -d ~/ponyNFT/assets/
    shdw-drive upload-multiple-files -kp ~/shdw-keypair.json -d ~/ponyNFT/assets/ --rpc <https://some-solana-api.com>
    shdw-drive edit-file --keypair ~/shdw-keypair.json --file ~/ponyNFT/01.json --url https://shdw-drive.genesysgo.net/abc123def456ghi789/0.json
    shdw-drive delete-file --keypair ~/shdw-keypair.json --url https://shdw-drive.genesysgo.net/abc123def456ghi789/0.json
    shdw-drive add-storage -kp ~/shdw-keypair.json -s 100MB
    shdw-drive reduce-storage -kp ~/shdw-keypair.json -s 500MB
    shdw-drive claim-stake -kp ~/shdw-keypair.json
    shdw-drive delete-storage-account ~/shdw-keypair.json
    shdw-drive undelete-storage-account -kp ~/shdw-keypair.json
    shdw-drive make-storage-account-immutable -kp ~/shdw-keypair.json
    shadow-drive-cli create-storage-account --name example_account --size 10MB
    shadow-drive-cli delete-storage-account --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli delete-storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli cancel-delete-storage-account --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli cancel-delete-storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli claim-stake --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli claim-stake FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli add-storage --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --size 10MB
    shadow-drive-cli add-storage FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB 10MB
    shadow-drive-cli add-immutable-storage --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --size 10MB
    shadow-drive-cli add-immutable-storage FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB 10MB
    shadow-drive-cli reduce-storage --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --size 10MB
    shadow-drive-cli reduce-storage FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB 10MB
    shadow-drive-cli make-storage-immutable --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli make-storage-immutable FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli get-storage-account --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli get-storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli get-storage-accounts --owner FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli get-storage-accounts
    shadow-drive-cli list-files --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli list-files FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB
    shadow-drive-cli get-text --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --filename example.txt
    shadow-drive-cli get-text FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB example.txt
    shadow-drive-cli get-object-data --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --file example.txt
    shadow-drive-cli get-object-data FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB example.txt
    shadow-drive-cli delete-file --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --filename example.txt
    shadow-drive-cli delete-file FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB example.txt
    shadow-drive-cli edit-file --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB --path /path/to/new/file.txt
    shadow-drive-cli edit-file FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB /path/to/new/file.txt
    shadow-drive-cli store-files --batch-size 100 --storage-account FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB file1.txt file2.txt
    shadow-drive-cli store-files FKDU64ffTrQq3E1sZsNknefrvY8WkKzCpRyRfptTnyvB file1.txt file2.txt
    Upload a FIle
    Upload Multiple FIles
    Edit a File
    Delete a File
    Add Storage
    Reduce Storage
    Make File Immutable
    The Defiant: Solana Crossroads Documentary
    Cover

    Support & FAQ

    Contents

    Getting Started

    Getting Started

    What is shdwDrive?

    shdwDrive is a decentralized mobile storage platform that allows you to store files securely while also providing opportunities to participate in the network as an operator.

    Basic Setup

    Q: How do I start using shdwDrive? A: Getting started is simple:

    1. Download and install the shdwDrive mobile app on your Android device

    2. Connect your wallet

    3. Create a storage bucket or choose a storage plan

    4. Begin uploading your files

    Q: Do I need a specific wallet to use shdwDrive? A: Yes, shdwDrive requires a Solana-compatible wallet. The app will guide you through connecting your preferred wallet during setup.

    Q: What happens after I connect my wallet? A: After connecting your wallet, you'll be guided through:

    1. A brief onboarding process

    2. Options to create storage space

    3. Access to the main dashboard where you can manage files and operator settings

    Q: What is a bucket? A: A bucket is your personal storage space on shdwDrive where you can store and organize your files. Think of it as your private folder in the decentralized network.

    Q: How do I create a bucket? A: To create a bucket:

    1. Open the shdwDrive app

    2. Look for the "Create Bucket" button on the home screen

    3. Follow the prompts to set up your new storage space

    Q: Is there an iOS app? A: No, not at this time. Android dominates global market share with approximately 70-75% of all smartphones worldwide, while iOS (iPhone) accounts for about 25-30%. We will complete the majority of our feature rollout on Android first, refining the user and operator experience, before moving to support the iOS device family.

    Q: Why do I need to authorize each file upload separately? A: Currently, each file upload requires a separate authorization through your wallet for security purposes. This is an intentional security feature that:

    • Ensures proper authorization of all file operations

    • Prevents unauthorized bulk uploads

    • Maintains a clear record of file ownership

    • Protects your data and storage space

    Q: Can I upload multiple files at once? A: At this time, files must be uploaded individually. Each upload requires:

    • Selecting the file

    • Authorizing the transaction through your wallet

    • Waiting for confirmation This process ensures proper tracking and verification of each file upload. Future updates will be introduce batch upload features while maintaining security standards.

    Q: What happens if my upload is interrupted? A: If an upload is interrupted:

    • The partial upload is automatically cancelled

    • No storage space is consumed

    • No transaction fee is charged

    • You can simply restart the upload Always ensure a stable connection when uploading larger files.

    Storage Plans

    Q: Is there a free storage option? A: Yes, shdwDrive offers a free 5GB storage plan to get started.

    Q: What can I store in shdwDrive? A: You can store various file types including:

    • Photos and images

    • Documents

    • Videos

    • Custom folders and file structures

    Q: Can I see how much storage I'm currently using? A: Yes, the app displays:

    • Your current storage usage

    • Available space in your bucket

    • Storage contribution level (if you're an operator)

    • Visual indicators of space usage

    Becoming an Operator

    As of release 1.0.8, gossip tickets have been refreshed. All current operators must verify their ticket has correctly updated after upgrade/install.

    Understanding Operators

    Q: What is a shdwDrive Operator? A: A shdwDrive Operator contributes storage space from their Android device to the decentralized network. Operators provide real utility by making their device's unused storage available for secure file storage.

    Q: What are the benefits of becoming an operator? A: As an operator, you:

    • Earn real revenue in USDC from user storage fees (in future releases)

    • Participate in a decentralized storage network

    • Contribute to network infrastructure

    • Receive programmatic revenue sharing based on your contribution

    Q: What's the difference between being a storage user and an operator? A: Key differences:

    • Users pay for storage services in USDC

    • Operators provide storage capacity to the network

    • SHDW tokens serve as operator collateral

    • Operators earn revenue from actual storage fees

    Requirements & Setup

    Q: What do I need to become an operator? A: To become an operator, you need:

    • An Android device (version 12.0L or higher)

    • Sufficient free storage space (minimum varies by contribution level)

    • Stable internet connection (WiFi required at this time)

    • A "Join Ticket" for network access

    Q: What are the recommended device requirements to run shdwDrive? A: To run shdwDrive effectively, your device should meet these specifications:

    • Android 12L or newer

    • Minimum 6 CPU cores

    • At least 8GB RAM

    • Sufficient free storage space for your chosen contribution level

    Q: Which devices are supported? A: Here's a non-comprehensive list of compatible devices:

    Premium/Flagship Devices

    • Google Pixel: 7, 7 Pro, 8, 8 Pro, 9, 9 Pro

    • Samsung Galaxy S: S22/+/Ultra, S23/+/Ultra, S24/+/Ultra

    • OnePlus: 10 Pro, 10T, 11, 11R

    • ASUS ROG Phone: 6, 6 Pro, 7, 7 Ultimate

    Mid-Range Devices

    • Google Pixel a-series: 6a, 7a

    • Samsung Galaxy A-series: A53 5G, A54 5G

    • OnePlus Nord: N20, N200

    • Motorola Edge (2022, 30)

    Specialty Devices (minimum storage spec)

    • Solana Saga

    • Seeker

    Q: Can I run shdwDrive on older devices? A: No. While older devices might run the app, we recommend meeting the minimum specifications for optimal performance and reliability. Devices that don't meet these specs may experience:

    • Slower proof generation

    • Reduced storage efficiency

    • Reduced revenue potential

    • Potential stability issues

    Q: Can I operate multiple nodes with the same wallet? A: No. Each wallet can only operate one node at a time. This means:

    • One wallet can only stake to one node

    • If you want to run multiple nodes, you'll need separate wallets for each

    • When switching devices, you must fully deactivate your current node before setting up the new one with the same wallet. Unstake before deactivating means waiting one Solana epoch before being able to withdraw.

    • When changing your storage allocation (therefore stake allocation), you must fully unstake, wait the cooldown of one Solana epoch, and restake up to your desired allocation.

    Q: What if I want to switch my node to a different device? A: To switch devices:

    • First deactivate and unstake on your current device

    • Wait the cooldown and complete the withdrawal process

    • Set up the new device using the same wallet, remembering only one wallet per one node

    • Stake and activate your new node

    Remember: Never try to run nodes on multiple devices with the same wallet as this can cause conflicts and operational issues at this time.

    Q: How much storage can I contribute? A: Storage contribution levels are based on 1 SHDW per ~51.2MB. Choose based on your device's available storage, with buffer for critical system, and desired SHDW stake level.

    Q: Why doesn't my device's full storage capacity show as available? A: Available storage is affected by several factors:

    • System files and OS requirements consume a portion of your total storage

    • Existing apps and data reduce available space

    • Android reserves space for system operations and updates

    • The app maintains a safety buffer to ensure stable operation

    For example, a 512GB device might show only 400GB as available because:

    • Android OS and pre-installed apps use ~50GB

    • System reserves ~30GB for updates and cache

    • Your personal apps and data use a portion

    • A safety margin is maintained for optimal performance

    The app shows only safely usable storage capacity to ensure reliable node operations.

    Q: Are external storage options such as Micro SD cards supported? A: Not at this time. While we have tested this feature and confirmed it to work, there is more work needed to ensure the shdwDrive runtime plays nice with how Android OS manages peripheral user storage.

    Node Operation

    Q: How do I pass verification to join the network? A: Use one of our many Join Ticket:

    Q: How do I manage my node? A: The Operator section of the app provides:

    • Node On/Off toggle

    • Current storage contribution level

    • Network connection status

    • Monitor logs

    Q: What does the operator interface show me? A: The operator interface displays:

    1. Current Status:

      • Storage Level: Your contributed storage amount

      • SHDW Collateral: Current network security deposit

    2. Node Controls:

    Q: What happens when I toggle my node on? A: When you toggle your node on:

    1. The app verifies your WiFi connection

    2. Connects to the network using your gossip ticket

    3. Begins participating in network operations

    4. Starts monitoring for storage requests Note: A stable WiFi connection is required to start your node

    Q: What should I check before starting my node? A: Before toggling your node on, ensure:

    1. You have a stable WiFi connection

    2. Your gossip ticket is properly entered

    3. The RPC is pre-filled correctly with an endpoint

    4. Your device is charged or plugged in

    Q: Can I run my node while using mobile data? A: Not at this time, but soon. Your node requires a WiFi connection to:

    • Maintain stable network connections

    • Ensure efficient data transfer

    • Reduce mobile data usage

    • Provide consistent network participation The node will automatically stop if WiFi connection is lost.

    Q: What is a Gossip Ticket? A: A Gossip Ticket is your node's access credential for joining the network. It contains necessary information for establishing secure connections with other nodes.

    Q: What are the storage fragments I see in my Downloads folder? A: These are secure storage units that your device uses to participate in the network. Each fragment (fragment.000, fragment.001, etc.) contains portions of the distributed storage system. Don't delete these manually - the app manages them automatically.

    Q: What's the process for changing my storage/stake level? A: Currently, changing levels requires:

    1. Deactivating your current node

    2. Unstaking your SHDW tokens

    3. Completing the withdrawal process

    4. Reactivating with your new desired storage level Direct storage updates are not supported - you must go through the full deactivation process.

    Q: Are there staking time requirements? A: Yes, there are important timing considerations:

    • Initial stake requires a warmup period (one Solana epoch)

    • Unstaking requires a cooldown period (one Solana epoch)

    • You cannot deactivate/unstake during the warmup period

    • Revenue can be claimed once per 24-hour period

    Q: What if I want to switch to a different device? A: When switching devices:

    1. Wait for your initial stake warmup period to complete

    2. Deactivate and unstake on your current device

    3. Wait for the cooldown period to complete

    4. Withdraw your SHDW tokens

    Technical Details

    Understanding Logs

    Q: How do I access and share node logs? A: Logs can be accessed through:

    1. The "View Logs" section in the operator dashboard

    2. Use "Copy Logs" to copy to clipboard

    3. Use "Send Logs" to share with support

    Q: What are the key log message types?

    Network Status Messages

    • Global View Updates:

      Shows network-wide state synchronization.

    • Event Processing:

      Indicates successful message processing.

    • Network Maintenance:

      Shows network optimization activities.

    Peer Management Messages

    • Join Operations:

      Indicates new peer verification.

    • Synchronization:

      Shows peer data synchronization.

    • Network Updates:

      Displays connection status changes.

    Q: What are Storage Proof logs? A: Storage Proof logs demonstrate that your node is actively storing and validating data as part of the decentralized network. These logs confirm that your node is fulfilling its role in maintaining data integrity. A typical storage proof log entry may look like this:

    Each entry shows:

    • A CID (Content Identifier) that uniquely identifies the data chunk.

    • The Operation performed (e.g., Generate).

    • A log message indicating that a Storage Proof has been processed.

    Note: Not every node will necessarily display storage proof logs. Whether or not these logs appear depends on the node’s active/passive view and the network topology—that is, whether your node is selected to participate in the storage proof process as part of the active set.

    Q: What indicates healthy node operation?

    Normal Operation Patterns

    1. Regular Network Activity:

      • Regular "Events Received" messages

      • Incrementing "Network Messages" count

      • Periodic "Global Consensus View" updates

    Warning Signs

    • Network Messages not incrementing

    • Frequent "Disconnect" messages with "unhealthy" status

    • High number of pending connections

    • No Global Consensus View updates

    Troubleshooting

    Q: Why won't my node connect? A: Common issues and solutions:

    • Check WiFi connection stability

    • Ensure Gossip Ticket is correct

    • Confirm device has sufficient storage

    • Check for any system power restrictions

    Q: What do I do if I see error messages? A: Common errors and fixes:

    • "Permission denied": Check app storage permissions

    • "Node disconnected": Check internet connection

    • "Storage allocation failed": Verify free space

    Q: Why did my node automatically turn off? A: Your node may automatically stop if:

    1. WiFi connection is lost

    2. Device battery is critically low

    3. Available storage drops below required level

    4. Network connection becomes unstable

    Q: How do I diagnose connection issues using logs?

    Initial Connection Problems

    1. Network Status:

      If edges are 0 or pending stays high, check:

      • Internet connection

      • Router UPnP settings

      • Port forwarding (30000-60000)

    Ongoing Operation Issues

    1. Network Isolation Signs:

      Solutions:

      • Verify gossip ticket

      • Check network configuration

      • Restart node if persistent

    Additional Note on Storage Proof Logs: While storage proof logs are a key indicator that your node is actively contributing to data storage and validation, their absence does not imply a problem. Depending on your node’s current role (active versus passive) and the overall network topology, you might not see these logs—even when your node is operating normally. As long as you observe the other critical indicators of network health (such as regular event updates and stable connection metrics), your node is functioning as expected.

    Q: What should I do if my node automatically stops? A: Common causes and solutions:

    1. Connection Loss:

      • WiFi disconnection

      • Network configuration changes

      • Router reboots Solution: Restore stable network connection

    Q: What should I do if my app becomes unresponsive or stuck at loading? A: If your app becomes unresponsive or stuck follow these recovery steps:

    1. Initial Recovery Steps:

      • Backup your node key if possible

      • Preserve the shdwDrive folder in Downloads

      • Uninstall the app

    Note: We are working on implementing direct node ID keypair restoration functionality for smoother recovery in future updates.

    Revenue & Operations

    Storage & Staking

    Q: How does the revenue model work? A: The revenue model is based on real utility:

    • Users pay storage fees in USDC

    • Fees are distributed to operators based on contribution

    • Revenue sharing is tied to actual storage provision

    • No artificial token rewards or inflation

    Q: What is the purpose of SHDW tokens? A: SHDW tokens serve specific functions:

    • Act as network collateral for operators

    • Enable slashing for malicious behavior

    • Support network security

    • Not designed for speculative value or rewards

    Q: How do I navigate the operator staking interface? A: The operator interface can be accessed through the Operator tab in the bottom navigation bar. Here you'll find:

    • Current Node Level and SHDW Staked amounts at the top

    • Deactivate & Unstake button for stopping your node

    • Withdraw button for accessing staked tokens after cooldown

    • Node On/Off toggle for controlling node operation

    Q: How do I check my current stake and storage levels? A: Your current status is displayed at the top of the Operator screen showing:

    • Current Node Level (storage amount)

    • Current SHDW Staked amount These values are automatically updated when changes occur.

    Q: How do I initiate the unstaking process? A: To unstake:

    1. Navigate to the Operator tab

    2. Click the "Deactivate & Unstake" button

    3. Confirm the transaction in your wallet

    4. Wait for the cooldown period

    Q: What are the available storage and staking tiers? A: Storage levels are now based on the amount of SHDW you choose to stake with a ratio of 1 SHDW per ~51.2MB and a min. floor of 1,000 SHDW to qualify.

    You must have sufficient SHDW tokens and available device storage to select a tier. All storage tiers require meeting the minimum device specifications outlined in the "Requirements & Setup" section.

    Note: Future releases will introduce more granular and dynamic staking and storage options.

    Q: What's the process for changing my storage level? A: Changing storage levels requires:

    1. Deactivating your current node

    2. Releasing your SHDW collateral

    3. Completing the withdrawal process

    4. Reactivating with new storage level and corresponding collateral

    Q: What happens during deactivation? A: The deactivation process includes:

    1. Node is stopped

    2. Current stake enters pending withdrawal state

    3. Storage allocation is released

    4. Node configuration is preserved for potential reactivation

    Q: What happens when I deactivate and unstake? A: The process involves several steps:

    1. Deactivate & Unstake: Initiates the unstaking process

    2. Withdrawal Period: Your tokens enter a pending withdrawal state

    3. Withdraw: After the cooling period, you can withdraw your staked SHDW

    4. Your node will be turned off but your operator account remains initialized

    Q: What happens to my revenues when I unstake? A: When unstaking:

    • Any unclaimed revenue remain available

    • You can still claim revenue from your previous contribution

    • New revenue stop accruing once unstaked

    • Your operator account retains access to claim functions

    Q: Are there staking time requirements? A: Yes, there are important timing considerations:

    • Initial stake requires a warmup period (one Solana epoch)

    • Unstaking requires a cooldown period (one Solana epoch)

    • You cannot deactivate/unstake during the warmup period

    • Revenue can be claimed once per 24-hour period

    Q: What if I want to switch to a different device? A: When switching devices:

    1. Wait for your initial stake warmup period to complete

    2. Deactivate and unstake on your current device

    3. Wait for the cooldown period to complete

    4. Withdraw your SHDW tokens

    Account Management

    Q: How do I change my storage contribution? A: To modify your contribution:

    1. Access the "Update Storage Contribution" section

    2. Select a new storage level

    3. Adjust stake if required

    4. Confirm the changes

    Q: What happens to my settings when I update my storage contribution? A: When updating your storage contribution:

    1. Your current node status is preserved

    2. The app verifies available device storage

    3. Stake requirements are recalculated

    4. You'll see a confirmation before changes apply

    Q: What happens if I want to stop being an operator? A: To deactivate:

    1. Use the "Deactivate & Unstake" option

    2. Your node will properly disconnect

    3. Storage fragments will be cleaned up

    4. Staked SHDW tokens will be returned

    Support & Maintenance

    Q: Where can I get help? A: Support resources:

    • In-app help buttons provide contextual guidance

    • View and share logs for technical support

    • Community forums and documentation

    • Official support channels

    Q: How do I report bugs or submit feedback? A: We have a dedicated system for bug reports and feedback:

    1. Visit our Airtable form:

    2. Fill out the relevant information

    3. Include any error messages or screenshots

    4. Describe the steps to reproduce the issue

    Q: What information should I include in a bug report? A: To help us resolve issues quickly, please include:

    • Your device model and Android version

    • App version number

    • Specific steps that led to the issue

    • Any error messages you received

    Q: How can I check if my bug has already been reported? A: Before submitting a new bug report:

    1. Check the FAQ for known issues and solutions

    2. Review recent app updates for fixed issues

    3. Look for similar issues in community discussions

    4. If in doubt, submit a new report - we prefer duplicate reports to missing issues

    Q: What happens after I submit feedback? A: After submission:

    1. Our team reviews all feedback and bug reports

    2. Critical issues are prioritized for immediate attention

    3. Feature requests are evaluated for future updates

    4. Common issues may be added to the FAQ

    Q: How do I backup my node information? A: Important backup steps:

    1. Save your keypair backup securely

    2. Document your node configuration

    3. Keep recovery phrases safe

    4. Never share private keys or sensitive data

    Q: What is the "Backup Key" option in my operator dashboard? A: The Backup Key feature:

    • Downloads your node's keypair information

    • Stores it securely in your downloads folder

    • Should be kept safe and private

    • Is essential for node recovery

    Q: When should I backup my node key? A: It's recommended to backup your key:

    1. Immediately after node activation

    2. Before making major node changes

    3. When updating the app

    4. As part of regular security maintenance Never share your backup key with anyone, even if they claim to be support.

    Q: Is my personal data safe when operating a node? A: Yes, the app uses encrypted storage and secure communication protocols. Your device's personal data is completely separated from the storage space you contribute to the network.

    Q: What information is shared with the network? A: Only technical information necessary for network operation is shared:

    • Your operator public key

    • Storage contribution metrics

    • Network connection details

    • Node performance statistics

    Allows you to monitor and control your storage usage

  • Enables us to better iterate the rolling out of more robust handling

  • Standard Android app level information is also accessible

    SHDW tokens for network collateral

  • Proper settings for the Android OS and app

  • Nothing Phone: 1, 2

  • Xiaomi POCO: X6, X6 Pro

  • Eventual slashing and malice tracking in the global view manager
    Maintain good WiFi connection

    "Deactivate & Release Collateral" button: Stops node operation

  • "Withdraw" button: Retrieves your SHDW collateral after cooldown

  • Node On/Off toggle: Controls active participation

  • Configuration:

    • Gossip Ticket: Network access credential

    • RPC Endpoint: Network connection point

  • Node Information:

    • Node ID: Your unique identifier

    • Backup Key: Recovery information

  • You have sufficient free storage space

    Withdrawal is only available after the cooldown period completes

    Set up the new device with the same wallet
  • Stake and activate your new node

  • Healthy Connection Status:

    • Stable or growing active edges

    • Low pending connection counts

    • Regular shuffle cycles

    • Successful peer syncs

  • Network Participation:

    • Regular cleanup operations

    • Periodic shuffle cycles

    • Consistent message processing

    • Stable peer count in consensus view

  • Frequent connection status changes
  • Missing periodic cleanup operations

  • Confirm proper port forwarding (30000-60000) and that you are not behind a restrictive firewall
    You run too low on memory
  • You swipe the app out of your active list, thereby hard closing it

  • You approve an Android system update that reboots your connections

  • Firewall restrictions

  • Peer Connection Issues:

    High pending counts indicate connection problems. Check:

    • Router settings

    • Network restrictions

    • Gossip ticket validity

  • Connection Quality Issues:

    Indicates:

    • Network instability

    • Connection timeouts

    • Possible firewall issues

    Device Issues:

    • Critical battery level

    • Insufficient storage

    • Memory constraints

    • App forced close Solution: Address resource constraints

  • System Changes:

    • Android OS updates

    • Security policy changes

    • Power management interventions Solution: Reconfigure after system changes

  • Install the latest version

  • Connect using your original wallet

  • Post-Installation Process:

    • The app will automatically show your existing stake status (stored on-chain)

    • You MUST complete the unstake process before creating a new stake:

      • Click "Deactivate & Unstake"

      • Wait for the cooldown period

      • Use "Withdraw" to claim your tokens

      • Only then proceed with new stake creation

  • Important Considerations:

    • Never attempt to create a new stake without unstaking first

    • Your original stake is accessible on-chain as long as you use the same wallet

    • Future versions will support direct node ID keypair restoration

    • New stake will generate a new node ID

  • Recovery Timeline:

    • Unstaking cooldown: One Solana epoch

    • Withdrawal availability: After cooldown

    • New stake activation: After withdrawal

  • Warning Signs During Recovery:

    • If your active stake doesn't appear after reinstall

    • If you see multiple stake positions

    • If unstake option isn't available Stop all operations and contact support immediately

  • Read more here

    Node ID and Backup Key options

  • Gossip Ticket and RPC Endpoint configuration

  • Use the "Withdraw" button to claim your tokens

    Withdrawal is only available after the cooldown period completes

    Set up the new device with the same wallet
  • Stake and activate your new node

  • Node may need to restart with new settings
    Earned revenue remain available for claim
    Report bugs through the app feedback system
    Submit the form for our team to review
    Screenshots if applicable
  • Node logs if the issue is operator-related

  • Major fixes are announced in app updates
    Use the app's built-in backup features
    Should never be shared with others
    What is shdwDrive?
    Basic Setup
    Storage Plans
    Becoming an Operator
    Understanding Operators
    Requirements & Setup
    Node Operation
    Join Tickets
    Technical Details
    Understanding Logs
    Network Architecture
    Troubleshooting
    Revenue & Operations
    Storage & Network Participation
    Account Management
    Support & Maintenance
    https://airtable.com/appUQgLU7dOMvGB5J/pagZ4dkosLyEqjvBs/form
    ├─ 🌐 Peer Disconnected
    ├─ Active: 1
    └─ Pending: 2
    ├─ 🌐 Disconnect
    └─ Status: unhealthy
    
    Ticket1:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAAB6X6SVVUSV32H6KQ4M7BNEMGCR4XTSVZTE4GBTEJQUZSJ7UTGKEJQBOE2MJ6AIAFQ5IAQDMXKAEBLJ2QBAO3OUAI=
    
    Ticket2:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAABEW6OOCQO7EMA7JKC6PQSK274ZBATDSWD2V7LJ6EA5BBCVN6HTF3QAL5ZVNGQCAEU5MAQ====
    
    Ticket3:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAABSU42N6PY6HFGSVBHDU7AOGSDUBY3KBCM3RQGNMVU7VNJWTF5TGFQAL5ZVNGQCAE65MAQ====
    
    Ticket4:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAABMI3Z7RMH4XD72UMXHBTRXXV2GGWJP75WYIFSBB5WSLZVBJADC5JAAL5ZVNGQCAFI5MAQ====
    
    Ticket5:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAABE34WB2TIQREIRXJRM6YUS2VS26BNDCO57HR3TBREHWNIK4O77MHAAL5ZVNGQCAFS5MAQ====
    
    Ticket6:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAAB7VYWWCU2O7S4QRMICVTDZNSUHV5B75F6XYHND4QKKSVTELF4F3FAAL5ZVNGQCAF45MAQ====
    
    Ticket 7:
    
    XHGA5LRCCKLCCAW3KCDU44SHKJEQFXZEYYKGS43BJRLSQV2ALH2QCAABIKUOVHCXUM45VSK6URD5KE3JJXAIQNSR65I5YC552CBRRAG6NMGQAL5ZVNGQCAGG5MAQ====
    
    ├─ 🌐 Global Consensus View
    ├─ peers: [number]
    └─ Network Messages: [number]
    ├─ Events Received
    └─ Network Messages: [number]
    ├─ ✂️ Cleanup
    └─ Network Msgs: [number]
    ├─ 🌐 Join
    └─ Proof: [true|false]
    ├─ 🔄 Recieved Peer Sync
    └─ Network Messages: [number]
    ├─ ⚙️ Network Update
    ├─ Active: [number]
    └─ Pending: [number]
    └─ CID: Hash("ab3f875881b3c9c04e3ee73f9a7fb1afac4466ba246e6da2d664cde23a4ef7d8")
    ├─ Operation: Generate
    [INFO] shdw_dht::dht_gossip: Storage Proof
    ├─ 🌐 graph edges=2 pending=1
    ├─ Events Received
    └─ Network Messages: [not incrementing]

    JavaScript

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

    Contents

    Getting Started: Javascript SDK

    Let's scaffold a React app and add our dependencies

    Review the SDK and resources.

    Instantiate the Wallet and Connection

    Use the if you need help. We're going to focus on ShdwDrive SDK in these docs, so if you need a primer on how to build a React site with Solana, we can refer you to other resources.

    Solana example code:

    Sample Code provided by Solana

    };

    Building components for various ShdwDrive operations

    Let's start by instantiating the ShdwDrive connection class object. This will have all ShdwDrive methods and it implements the signing wallet within the class for all transactions.

    At the simplest level, it is recommend for a React app to immediately try to load a connection to a user's ShdwDrives upon wallet connection. This can be done with the useEffect React hook.

    This can be done with a NodeJS + TypeScript program as well.

    Create a Storage Account

    This implementation is effectively the same for both Web and Node implementations. There are three params that are required to create a storage account:

    • name: a friendly name for your storage account

    • size: The size of your storage accounts with a human readable ending containing KB, MB, or GB

    Get a list of Owned Storage Accounts

    This implementation is effectively the same for both Web and Node implementations. The only parameter required is either v1 or v2 for the version of storage account you created in the previous step.

    Full Response:

    Output

    Get a Specific Storage Account

    This implementation is effectively the same for both Web and Node implementations. The only parameter required is either a PublicKey object or a base-58 string of the public key.

    Full Response:

    Output

    Upload a File

    The uploadFile method requires two parameters:

    • key: A PublicKey object representing the public key of the Shdw Storage Account

    • data: A file of either the File object type or ShadowFile object type

    Check the intellisense popup below when hovering over the method

    File objects are implemented in web browsers, and ShadowFile is a custom type we implemented in TypeScript. So either you are using File in the web, or you are scripting in TS.

    Here is an example with a React Component:

    React Upload Component

    And a NodeJS + TypeScript implementation would look like:

    TS UploadFile

    Upload Multiple Files

    This is a nearly identical implementation to uploadFile, except that it requires a FileList or array of ShadowFiles and an optional concurrency parameter.

    Recall that the default setting is to attempt to upload 3 files concurrently. Here you can override this and specify how many files you want to try to upload based on the cores and bandwith of your infrastructure.

    Delete a File

    The implementation of deleteFile is the same between web and Node. There are three required parameters to delete a file:

    • key: the storage account's public key

    • url: the current URL of the file to be deleted

    • version: can be either v1 or v2

    Edit a File (aka Replace a file)

    The editFile method is a combo of uploadFile and deleteFile. Let's look at the params:

    • key: the Public Key of the storage account

    • url: the URL of the file that is being replaced

    • data: the file that is replacing the current file. It must have the exact same filename and extension, and it must be a

    List Storage Account Files (aka List Objects)

    This is a simple implementation that only requires a public key to get the file names of a storage account.

    And the response payload:

    Increase Storage Account Size

    This is a method to simply increase the storage limit of a storage account. It requires three params:

    • key: storage account public key

    • size: amount to increase by, must end with KB, MB, or GB

    Reduce Storage Account Size

    This is a method to decrease the storage limit of a storage account. This implementation only requires three params - the storage account key, the amount to reduce it by, and the version.

    Next you'll want to claim your unused SHDW

    This method allows you to reclaim the SHDW that is no longer being used. This method only requires a storage account public key and a version.

    Delete a Storage Account

    As the name implies, you can delete a storage account and all of its files. The storage account can still be recovered until the current epoch ends, but after that, it will be removed. This implementation only requires two params - a storage account key and a version.

    Undelete the Deleted Storage Account

    You can still get your storage account back if the current epoch hasn't elapsed. This implementation only requires two params - an account public key and a version.

    Methods

    constructor

    Definition

    This method is used to create a new instance of the ShdwDrive class. It accepts a web3 connection object and a web3 wallet. It returns an instance of the ShdwDrive class.

    Parameters

    • connection: Connection - initialized web3 connection object

    • wallet: any - Web3 wallet

    Returns

    It returns an instance of the ShdwDrive class.

    addStorage

    Definition

    addStorage is a method of the ShadowDrive class defined in index.ts at line 121. It takes three parameters: key, size, and version and returns a Promise<ShadowDriveResponse> with the confirmed transaction ID.

    Parameters

    • key: PublicKey - Public Key of the existing storage to increase size on

    • size: string - Amount of storage you are requesting to add to your storage account. Should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported currently.

    Returns

    Confirmed transaction ID

    cancelDeleteStorageAccount

    Definition

    Implementation of cancelDeleteStorageAccount defined in index.ts:135 This method is used to cancel a delete request for a Storage Account on ShdwDrive. It accepts a Public Key of the Storage Account and the ShdwDrive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the undelete request.

    Parameters

    • key: PublicKey - Publickey

    Returns

    Confirmed transaction ID

    claimStake

    Definition

    This method is used to request a Stake on ShdwDrive. It accepts a PublicKey of the Storage Account and the ShdwDrive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the claimStake request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • version: `ShadowDrive

    Returns

    Confirmed transaction ID

    createStorageAccount

    Definition

    Implementation of ShdwDrive.createStorageAccount defined in index.ts:120 This method is used to create a new Storage Account on ShdwDrive. It accepts the name of the Storage Account, the size of the requested Storage Account, and the ShdwDrive version (v1 or v2). It also accepts an optional secondary owner for the Storage Account. It returns a Promise containing the created Storage Account and the transaction signature.

    Parameters

    • name: string - What you want your storage account to be named. (Does not have to be unique)

    • size: string - Amount of storage you are requesting to create. Should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported currently.

    Returns

    deleteFile

    Definition

    This method is used to delete a file on ShdwDrive. It accepts a Public Key of your Storage Account, the ShdwDrive URL of the file you are requesting to delete and the ShdwDrive version (v1 or v2). It returns a Promise containing the confirmed transaction ID of the delete request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • url: string - ShdwDrive URL of the file you are requesting to delete.

    • version

    Returns

    deleteStorageAccount

    Definition

    Implementation of ShadowDrive.deleteStorageAccount defined in index.ts:124 This method is used to delete a Storage Account on ShdwDrive. It accepts a Public Key of the Storage Account and the ShdwDrive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the delete request.

    Parameters

    • key: PublicKey - Publickey of a Storage Account

    • version: ShadowDriveVersion - ShdwDrive version (v1 or v2)

    Returns

    Confirmed transaction ID

    editFile

    Definition

    This method is used to edit a file on ShdwDrive. It accepts a Public Key of your Storage Account, the URL of the existing file, the File or ShadowFile object, and the ShdwDrive version (v1 or v2). It returns a Promise containing the file location and the transaction signature.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • url: string - URL of existing file

    • data

    Returns

    getStorageAccount

    Definition

    This method is used to get the details of a Storage Account on ShdwDrive. It accepts a Public Key of the Storage Account and returns a Promise containing the Storage Account details.

    Parameters

    • key: PublicKey - Publickey of a Storage Account

    Returns

    getStorageAccounts

    Definition

    This method is used to get a list of all the Storage Accounts associated with the current user. It accepts a ShdwDrive version (v1 or v2). It returns a Promise<StorageAccountResponse[]> containing the list of storage accounts.

    Parameters

    • version: ShadowDriveVersion - ShdwDrive version (v1 or v2)

    Returns

    listObjects

    Definition

    This method is used to list the Objects in a Storage Account on ShdwDrive. It accepts a Public Key of the Storage Account and returns a Promise containing the list of Objects in the Storage Account.

    Parameters

    • storageAccount: PublicKey

    Returns

    makeStorageImmutable

    Definition

    This method is used to make a Storage Account immutable on ShdwDrive. It accepts a Public Key of the Storage Account and the ShdwDrive version (v1 or v2). It returns a Promise containing the confirmed transaction ID of the makeStorageImmutable request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • version: ShadowDriveVersion - ShdwDrive version (v1 or v2)

    Returns

    migrate

    Definition

    This method is used to migrate a Storage Account on ShdwDrive. It accepts a PublicKey of the Storage Account. It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the migration request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    Returns

    Confirmed transaction ID

    redeemRent

    Definition

    This method is used to redeem Rent on ShdwDrive. It accepts a Public Key of the Storage Account and the Public Key of the file account to close. It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the redeemRent request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • fileAccount: PublicKey - PublicKey of the file account to close

    Returns

    Confirmed transaction ID

    reduceStorage

    Definition

    This method is used to reduce the storage of a Storage Account on ShdwDrive. It accepts a Public Key of the Storage Account, the amount of storage you are requesting to reduce from your storage account, and the ShdwDrive version (v1 or v2). It returns a Promise containing the confirmed transaction ID of the reduce storage request.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • size: string - Amount of storage you are requesting to reduce from your storage account. Should be in a string like '1KB', '1MB', '1GB'. Only KB, MB, and GB storage delineations are supported currently.

    Returns

    storageConfigPDA

    Definition

    This exposes the PDA account in case developers have a need to display / use the data stored in the account.

    Parameters

    • key: PublicKey - Publickey of Storage Account

    • data: File | ShadowFile - File or ShadowFile object, file extensions should be included in the name property of ShadowFiles.

    Returns

    Public Key

    refreshStake

    Definition

    This method is used to update your storage account's stake amount. It is required to call this method after calling the method in order for your stage account to update properly.

    Parameters

    • key: PublicKey - Publickey of the Storage Account

    • version: can be either v1 or v2. Note - v1 is completely deprecated and you shuold only use v2 moving forward.

    Returns

    topUp

    Definition

    This method is used to top up a storage account's $SHDW balance to cover any necessary fees, like mutable storage fees which are collected every epoch. It is necessary to call the `` method after this.

    Parameters

    • key: PublicKey - Publickey of the Storage Account

    • amount: Number - Amount of $SHDW to transfer to the stake account

    Returns

    uploadFile

    Definition

    This method is used to upload a file to ShdwDrive. It accepts a Public Key of your Storage Account and a File or ShadowFile object. The file extensions should be included in the name property of ShadowFiles. It returns a Promise containing the file location and the transaction signature.

    Parameters

    • key: PublicKey - Publickey of Storage Account.

    • data: File | ShadowFile - File or ShadowFile object, file extensions should be included in the name property of ShadowFiles.

    Returns

    uploadMultipleFiles

    Definition

    This method is used to upload multiple files to a Storage Account on ShdwDrive. It accepts the Storage Account's PublicKey, a data object containing the FileList or ShadowFile array of files to upload, an optional concurrent number for the number of files to concurrently upload, and an optional callback function for every batch of files uploaded. It returns a Promise<ShadowBatchUploadResponse[]> containing the file names, locations and transaction signatures for uploaded files.

    Parameters

    • key: PublicKey - Storage account PublicKey to upload the files to.

    • data: FileList | ShadowFile[] -

    • concurrent

    Returns

    userInfo

    Definition

    userInfo: PublicKey

    Example - Using POST API requests via the Javascript SDK to make an account immutable with solana transaction signing

    Help improve this or provide us with .

    claimStake

  • createStorageAccount

  • deleteFile

  • deleteStorageAccount

  • editFile

  • getStorageAccount

  • getStorageAccounts

  • listObjects

  • makeStorageImmutable (updated)

  • migrate

  • redeemRent

  • reduceStorage (updated)

  • storageConfigPDA

  • refreshStake (new)

  • topUp (new)

  • uploadFile

  • uploadMultipleFiles

  • userInfo

  • version: can be either v1 or v2. Note - v1 is completely deprecated and you shuold only use v2 moving forward.
    File
    or
    ShadowFile
    object
  • version: either v1 or v2

  • version: storage account version, must be v1 or v2
    version: ShadowDriveVersion - ShadowDrive version (v1 or v2)
    version: ShadowDriveVersion - ShdwDrive version(v1 or v2)
  • owner2 (optional): PublicKey - Optional secondary owner for the storage account.

  • : `ShdwDrive
  • Version` - ShdwDrive version (v1 or v2)

  • :
    File | ShadowFile
    - File or ShadowFile object, file extensions should be included in the name property of ShadowFiles.
  • version: ShadowDriveVersion - ShdwDrive version (v1 or v2)

  • version
    :
    ShadowDriveVersion
    - ShdwDrive version (v1 or v2)
    (optional):
    number
    - Number of files to concurrently upload. Default: 3
  • callback (optional): Function - Callback function for every batch of files uploaded. A number will be passed into the callback like callback(num) indicating the number of files that were confirmed in that specific batch.

  • Getting Started
    Create a Storage Account
    Upload a File
    Edit a File
    Delete a File
    Methods
    constructor
    addStorage
    cancelDeleteStorageAccount
    Example - POST request via SDK
    Solana Web3.js
    Solana API
    Solana docs and examples here
    `topUp`
    refreshStake
    resource
    feedback
    npx create-react-app shdwapp
    cd shdwapp/
    yarn add @shadow-drive/sdk @project-serum/anchor \
        @solana/wallet-adapter-base \
        @solana/wallet-adapter-react \
        @solana/wallet-adapter-react-ui \
        @solana/wallet-adapter-wallets \
        @solana/web3.js \
        @solana-mobile/wallet-adapter-mobile
    import { WalletNotConnectedError } from '@solana/wallet-adapter-base';
    import { useConnection, useWallet } from '@solana/wallet-adapter-react';
     import { Keypair, SystemProgram, Transaction } from '@solana/web3.js';
     import React, { FC, useCallback } from 'react';
    export const SendSOLToRandomAddress: FC = () => {
    const { connection } = useConnection();
    const { publicKey, sendTransaction } = useWallet();
    const onClick = useCallback(async () => {
        if (!publicKey) throw new WalletNotConnectedError();
    
        // 890880 lamports as of 2022-09-01
        const lamports = await connection.getMinimumBalanceForRentExemption(0);
    
        const transaction = new Transaction().add(
            SystemProgram.transfer({
                fromPubkey: publicKey,
                toPubkey: Keypair.generate().publicKey,
                lamports,
            })
        );
    
        const {
            context: { slot: minContextSlot },
            value: { blockhash, lastValidBlockHeight }
        } = await connection.getLatestBlockhashAndContext();
    
        const signature = await sendTransaction(transaction, connection, { minContextSlot });
    
        await connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature });
    }, [publicKey, sendTransaction, connection]);
    
    return (
        <button onClick={onClick} disabled={!publicKey}>
            Send SOL to a random address!
        </button>
    );
    import React, { useEffect } from "react";
    import * as anchor from "@project-serum/anchor";
    import {ShdwDrive} from "@shadow-drive/sdk";
    import { useWallet, useConnection } from "@solana/wallet-adapter-react";
    
    export default function Drive() {
        const { connection } = useConnection();
        const wallet = useWallet();
        useEffect(() => {
            (async () => {
                if (wallet?.publicKey) {
                    const drive = await new ShdwDrive(connection, wallet).init();
                }
            })();
        }, [wallet?.publicKey])
        return (
            <div></div>
    const anchor = require("@project-serum/anchor");
    const { Connection, clusterApiUrl, Keypair } = require("@solana/web3.js");
    const { ShdwDrive } = require("@shadow-drive/sdk");
    const key = require("./shdwkey.json");
    
    async function main() {
        let secretKey = Uint8Array.from(key);
        let keypair = Keypair.fromSecretKey(secretKey);
        const connection = new Connection(
            clusterApiUrl("mainnet-beta"),
            "confirmed"
        );
        const wallet = new anchor.Wallet(keypair);
        const drive = await new ShdwDrive(connection, wallet).init();
    }
    
    main();
    //create account
    const newAcct = await drive.createStorageAccount("myDemoBucket", "10MB", "v2");
    console.log(newAcct);
    const accts = await drive.getStorageAccounts("v2");
    // handle printing pubKey of first storage acct
    let acctPubKey = new anchor.web3.PublicKey(accts[0].publicKey);
    console.log(acctPubKey.toBase58());
    [
      {
        publicKey: PublicKey {
          _bn: <BN: c9217d175c7257f52a64cb9faa203e65487343720441cef2d9abcb3f8c706053>
        },
        account: {
          immutable: false,
          toBeDeleted: false,
          deleteRequestEpoch: 0,
          storage: <BN: a00000>,
          owner1: [PublicKey],
          accountCounterSeed: 0,
          creationTime: 1665690481,
          creationEpoch: 359,
          lastFeeEpoch: 359,
          identifier: 'myDemoBucket'
        }
      },
      {
        publicKey: PublicKey {
          _bn: <BN: 502aa038c1b95a8bc36c301f3ae06beda929ec3a044b0543e70d4378d4c574ea>
        },
        account: {
          immutable: false,
          toBeDeleted: false,
          deleteRequestEpoch: 0,
          storage: <BN: a00000>,
          owner1: [PublicKey],
          accountCounterSeed: 1,
          creationTime: 1665690563,
          creationEpoch: 359,
          lastFeeEpoch: 359,
          identifier: 'myDemoBucket'
        }
      }
    ]
    const acct = await drive.getStorageAccount(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    console.log(acct);
    {
      storage_account: 'EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN',
      reserved_bytes: 10485760,
      current_usage: 0,
      immutable: false,
      to_be_deleted: false,
      delete_request_epoch: 0,
      owner1: 'GXE2RHkdivb7pdBykiAr9mpKy1kHF1Qe7oZqnX4NwkoD',
      account_counter_seed: 0,
      creation_time: 1665690481,
      creation_epoch: 359,
      last_fee_epoch: 359,
      identifier: 'myDemoBucket',
      version: 'V2'
    }
    import React, { useState } from "react";
    import { ShdwDrive } from "@shadow-drive/sdk";
    import { useWallet, useConnection } from "@solana/wallet-adapter-react";
    import FormData from "form-data";
    
    export default function Upload() {
        const [file, setFile] = useState < File > undefined;
        const [uploadUrl, setUploadUrl] = useState < String > undefined;
        const [txnSig, setTxnSig] = useState < String > undefined;
        const { connection } = useConnection();
        const wallet = useWallet();
        return (
            <div>
                <form
                    onSubmit={async (event) => {
                        event.preventDefault();
                        const drive = await new ShdwDrive(
                            connection,
                            wallet
                        ).init();
                        const accounts = await drive.getStorageAccounts();
                        const acc = accounts[0].publicKey;
                        const getStorageAccount = await drive.getStorageAccount(
                            acc
                        );
    
                        const upload = await drive.uploadFile(acc, file);
                        console.log(upload);
                        setUploadUrl(upload.finalized_location);
                        setTxnSig(upload.transaction_signature);
                    }}
                >
                    <h1>ShdwDrive File Upload</h1>
                    <input
                        type="file"
                        onChange={(e) => setFile(e.target.files[0])}
                    />
                    <br />
                    <button type="submit">Upload</button>
                </form>
                <span>You may have to wait 60-120s for the URL to appear</span>
                <div>
                    {uploadUrl ? (
                        <div>
                            <h3>Success!</h3>
                            <h4>URL: {uploadUrl}</h4>
                            <h4>Sig: {txnSig}</h4>
                        </div>
                    ) : (
                        <div></div>
                    )}
                </div>
            </div>
        );
    }
    const { ShdwDrive, ShadowFile } = require("@shadow-drive/sdk");
    /**
     * ...
     * Initialize storage account here...
     * ...
     */
    const fileBuff = fs.readFileSync("./mytext.txt");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const fileToUpload: ShadowFile = {
        name: "mytext.txt",
        file: fileBuff,
    };
    const uploadFile = await drive.uploadFile(acctPubKey, fileToUpload);
    console.log(uploadFile);
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const delFile = await drive.deleteFile(acctPubKey, url, "v2");
    console.log(delFile);
    const fileToUpload: ShadowFile = {
        name: "mytext.txt",
        file: fileBuff,
    };
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const editFile = await drive.editFile(acctPubKey, url, "v2", fileToUpload);
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const listItems = await drive.listObjects(acctPubKey);
    console.log(listItems);
    { keys: [ 'index.html' ] }
    const accts = await drive.getStorageAccounts("v2");
    let acctPubKey = new anchor.web3.PublicKey(accts[1].publicKey);
    const addStgResp = await drive.addStorage(acctPubKey, "10MB", "v2");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const shrinkAcct = await drive.reduceStorage(acctPubKey, "10MB", "v2");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const claimStake = await drive.claimStake(acctPubKey, "v2");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const delAcct = await drive.deleteStorageAccount(acctPubKey, "v2");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const cancelDelStg = await drive.cancelDeleteStorageAccount(acctPubKey, "v2");
    const shadowDrive = new ShadowDrive(connection, wallet).init();
    // Javascript SDK example using the constructor method
    // this creates a new instance of the ShadowDrive class and initializes it with the given connection and wallet parameters
    const shadowDrive = new ShadowDrive(connection, wallet).init();
    {
      message: string;
      transaction_signature?: string
    }
    const accts = await drive.getStorageAccounts("v2");
    let acctPubKey = new anchor.web3.PublicKey(accts[1].publicKey);
    const addStgResp = await drive.addStorage(acctPubKey, "10MB", "v2");
    // Javascript SDK example using the addStorage method
    // This line retrieves the storage accounts with version "v2" using the `getStorageAccounts` method of the `drive` object and stores them in the `accts` variable.
    const accts = await drive.getStorageAccounts("v2")
    
    // This line creates a new `PublicKey` object using the public key of the second storage account retrieved in the previous line and stores it in the `acctPubKey` variable.
    let acctPubKey = new anchor.web3.PublicKey(accts[1].publicKey)
    
    // This line adds a new storage allocation of size "10MB" and version "v2" to the storage account identified by the public key in `acctPubKey`. The response is stored in the `addStgResp` variable.
    const addStgResp = await drive.addStorage(acctPubKey,"10MB","v2"ca
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const cancelDelStg = await drive.cancelDeleteStorageAccount(acctPubKey, "v2");
    // Javascript SDK example using the cancelDeleteStorageAccount method
    // Create a new public key object from a string representation of a Solana account public key
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Call the "cancelDeleteStorageAccount" function of the ShdwDrive API, passing in the account public key object and a string indicating the storage account version to cancel deletion for
    const cancelDelStg = await drive.cancelDeleteStorageAccount(acctPubKey, "v2");
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const claimStake = await drive.claimStake(acctPubKey, "v2");
    // Javascript SDK example using the claimStake method
    // Create a new public key object with the specified value
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Call the 'claimStake' function on the 'drive' object with the account public key and 'v2' as parameters, and wait for its completion before proceeding
    const claimStake = await drive.claimStake(acctPubKey, "v2");
    {
        "shdw_bucket": String,
        "transaction_signature": String
    }
    //create account
    const newAcct = await drive.createStorageAccount("myDemoBucket", "10MB", "v2");
    console.log(newAcct);
    // Javascript SDK example using the createStorageAccount method
    // Calls the 'createStorageAccount' function on the 'drive' object with "myDemoBucket", "10MB", and "v2" as parameters, and waits for its completion before proceeding. The result of the function call is assigned to the 'newAcct' variable.
    const newAcct = await drive.createStorageAccount("myDemoBucket", "10MB", "v2");
    
    // Logs the value of the 'newAcct' variable to the console
    console.log(newAcct);
    {
        "message": String,
        "error": String or not passed if no error
    }
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const delFile = await drive.deleteFile(acctPubKey, url, "v2");
    console.log(delFile);
    // Javascript SDK example using the deleteFile method
    // Assigns a string value containing the URL of the file to be deleted to the 'url' variable
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    
    // Creates a new public key object with a specific value and assigns it to the 'acctPubKey' variable
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Calls the 'deleteFile' function on the 'drive' object with the account public key, URL, and "v2" as parameters, and waits for its completion before proceeding. The result of the function call is assigned to the 'delFile' variable.
    const delFile = await drive.deleteFile(acctPubKey, url, "v2");
    
    // Logs the value of the 'delFile' variable to the console
    console.log(delFile);
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const delAcct = await drive.deleteStorageAccount(acctPubKey, "v2");
    // Javascript SDK example using the deleteStorageAccount method
    // Creates a new public key object with a specific value and assigns it to the 'acctPubKey' variable
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Calls the 'deleteStorageAccount' function on the 'drive' object with the account public key and "v2" as parameters, and waits for its completion before proceeding. The result of the function call is assigned to the 'delAcct' variable.
    const delAcct = await drive.deleteStorageAccount(acctPubKey, "v2");
    {
      finalized_location: string;
    }
    const fileToUpload: ShadowFile = {
        name: "mytext.txt",
        file: fileBuff,
    };
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const editFile = await drive.editFile(acctPubKey, url, "v2", fileToUpload);
    // Javascript SDK example using the editFile method
    // Creates an object containing the name and content of the file to upload and assigns it to the 'fileToUpload' variable
    const fileToUpload: ShadowFile = {
        name: "mytext.txt",
        file: fileBuff,
    };
    
    // Assigns a string value containing the URL of the file to be edited to the 'url' variable
    const url =
        "https://shdw-drive.genesysgo.net/4HUkENqjnTAZaUR4QLwff1BvQPCiYkNmu5PPSKGoKf9G/fape.png";
    
    // Creates a new public key object with a specific value and assigns it to the 'acctPubKey' variable
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Calls the 'editFile' function on the 'drive' object with the account public key, URL, "v2", and the file object as parameters, and waits for its completion before proceeding. The result of the function call is assigned to the 'editFile' variable.
    const editFile = await drive.editFile(acctPubKey, url, "v2", fileToUpload);
    {
      storage_account: PublicKey;
      reserved_bytes: number;
      current_usage: number;
      immutable: boolean;
      to_be_deleted: boolean;
      delete_request_epoch: number;
      owner1: PublicKey;
      account_counter_seed: number;
      creation_time: number;
      creation_epoch: number;
      last_fee_epoch: number;
      identifier: string;
      version: `${Uppercase<ShadowDriveVersion>}`;
    }
    const acct = await drive.getStorageAccount(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    console.log(acct);
    // Javascript SDK example using the getStorageAccount method
    // Calls the 'getStorageAccount' function on the 'drive' object with the account public key as a parameter, and waits for its completion before proceeding. The result of the function call is assigned to the 'acct' variable.
    const acct = await drive.getStorageAccount(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Logs the resulting object to the console
    console.log(acct);
    {
      publicKey: anchor.web3.PublicKey;
      account: StorageAccount;
    }
    ShadowDrive.getStorageAccounts(shadowDriveVersion)
        .then((storageAccounts) =>
            console.log(`List of storage accounts: ${storageAccounts}`)
        )
        .catch((err) => console.log(`Error getting storage accounts: ${err}`));
    // Javascript SDK example using the getStorageAccounts method
    // Calls the 'getStorageAccounts' function on the 'drive' object with the version parameter "v2", and waits for its completion before proceeding. The result of the function call is assigned to the 'accts' variable.
    const accts = await drive.getStorageAccounts("v2");
    
    // Uses the 'let' keyword to declare a variable 'acctPubKey', which is assigned the value of the publicKey of the first object in the 'accts' array. This value is converted to a string in Base58 format.
    let acctPubKey = new anchor.web3.PublicKey(accts[0].publicKey);
    console.log(acctPubKey.toBase58());
    {
      keys: string[];
    }
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const listItems = await drive.listObjects(acctPubKey);
    console.log(listItems);
    // Javascript SDK example using the listObjects method
    // Creates a new 'PublicKey' object using a specific public key string and assigns it to the 'acctPubKey' variable.
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Calls the 'listObjects' function on the 'drive' object with the 'acctPubKey' variable as a parameter, and waits for its completion before proceeding. The result of the function call is assigned to the 'listItems' variable.
    const listItems = await drive.listObjects(acctPubKey);
    
    // Logs the resulting object to the console.
    console.log(listItems);
    {
      message: string;
      transaction_signature?: string;
    }
    const key = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const result = await drive.makeStorageImmutable(key, "v2");
    console.log(result);
    // Javascript SDK example using the makeStorageImmutable method
    // Create a new PublicKey object using a public key string.
    const key = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Call the makeStorageImmutable function with the PublicKey object and a version string, and wait for it to complete.
    const result = await drive.makeStorageImmutable(key, "v2");
    
    // Log the resulting object to the console.
    console.log(result);
    const result = await drive.migrate(key);
    // Javascript SDK example using the migrate method
    // Call the migrate function on the drive object, passing in the PublicKey object as a parameter.
    const result = await drive.migrate(key);
    const fileAccount = new anchor.web3.PublicKey(
        "3p6U9s1sGLpnpkMMwW8o4hr4RhQaQFV7MkyLuW8ycvG9"
    );
    const result = await drive.redeemRent(key, fileAccount);
    // Javascript SDK example using the redeemRent method
    // Create a new PublicKey object using a public key string for the file account.
    const fileAccount = new anchor.web3.PublicKey(
        "3p6U9s1sGLpnpkMMwW8o4hr4RhQaQFV7MkyLuW8ycvG9"
    );
    
    // Call the redeemRent function on the drive object, passing in both PublicKey objects as parameters.
    const result = await drive.redeemRent(key, fileAccount);
    {
      message: string;
      transaction_signature?: string;
    }
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const shrinkAcct = await drive.reduceStorage(acctPubKey, "10MB", "v2");
    // Javascript SDK example using the reduceStorage method
    // Create a new public key object with the given string
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Reduce the storage size of the storage account with the given public key
    // to 10MB using the version specified
    const shrinkAcct = await drive.reduceStorage(acctPubKey, "10MB", "v2");
    storageConfigPDA: PublicKey;
    //storageConfigPDA is a method in the Shdw SDK that returns the public key of the program derived account (PDA) for the Shdw storage program's config. A program derived account is a special account on the Solana blockchain that is derived from a program's public key and a specific seed. The purpose of this method is to provide a convenient way to obtain the PDA for the Shdw storage program's config. The config contains important information such as the current storage rent exemption threshold and the data size limits for storage accounts. This public key can be used to interact with the Shdw storage program's config account, allowing the user to retrieve and modify the program's global configuration settings.
    storageConfigPDA: PublicKey;
    {
        txid: string
    }
    {
        txid: string;
    }
    {
      finalized_locations: Array<string>;
      message: string;
      upload_errors: Array<UploadError>;
    }
    const uploadFile = await drive.uploadFile(acctPubKey, fileToUpload);
    // Javascript SDK example of the uploadFile method
    // This line calls the uploadFile method of the drive object and passes in two parameters:
    // 1. acctPubKey: A PublicKey object representing the public key of the storage account where the file will be uploaded.
    // 2. fileToUpload: A ShadowFile object containing the file name and file buffer to be uploaded.
    const uploadFile = await drive.uploadFile(acctPubKey, fileToUpload);
    {
      fileName: string;
      status: string;
      location: string;
    }
    const drive = new ShadowDrive();
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    const files = [
        {
            name: "file1.txt",
            file: new File(["hello"], "file1.txt"),
        },
        {
            name: "file2.txt",
            file: new File(["world"], "file2.txt"),
        },
        {
            name: "file3.txt",
            file: new File(["!"], "file3.txt"),
        },
    ];
    const concurrentUploads = 2;
    const callback = (response) => {
        console.log(`Uploaded file ${response.fileIndex}: ${response.fileName}`);
    };
    const responses = await drive.uploadMultipleFiles(
        acctPubKey,
        files,
        concurrentUploads,
        callback
    );
    console.log(responses);
    // Javascript SDK example of the uploadMultipleFiles method
    // Create an instance of the ShdwDrive client
    const drive = new ShadowDrive();
    
    // Define the public key of the storage account where the files will be uploaded
    const acctPubKey = new anchor.web3.PublicKey(
        "EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
    );
    
    // Define an array of files to upload
    const files = [
        {
            name: "file1.txt",
            file: new File(["hello"], "file1.txt"),
        },
        {
            name: "file2.txt",
            file: new File(["world"], "file2.txt"),
        },
        {
            name: "file3.txt",
            file: new File(["!"], "file3.txt"),
        },
    ];
    
    // Define the maximum number of concurrent uploads (optional)
    const concurrentUploads = 2;
    
    // Define a callback function to be called after each file is uploaded (optional)
    const callback = (response) => {
        console.log(`Uploaded file ${response.fileIndex}: ${response.fileName}`);
    };
    
    // Call the uploadMultipleFiles method to upload all the files
    const responses = await drive.uploadMultipleFiles(
        acctPubKey,
        files,
        concurrentUploads,
        callback
    );
    
    // Print the responses returned by the server for each file uploaded
    console.log(responses);
    // Import required modules and constants
    import * as anchor from "@project-serum/anchor";
    import { getStakeAccount, findAssociatedTokenAddress } from "../utils/helpers";
    import {
      emissions,
      isBrowser,
      SHDW_DRIVE_ENDPOINT,
      tokenMint,
      uploader,
    } from "../utils/common";
    import {
      ASSOCIATED_TOKEN_PROGRAM_ID,
      TOKEN_PROGRAM_ID,
    } from "@solana/spl-token";
    import { ShadowDriveVersion, ShadowDriveResponse } from "../types";
    import fetch from "node-fetch";
    
    /**
     *
     * @param {anchor.web3.PublicKey} key - Publickey of a Storage Account
     * @param {ShadowDriveVersion} version - ShadowDrive version (v1 or v2)
     * @returns {ShadowDriveResponse} - Confirmed transaction ID
     */
    export default async function makeStorageImmutable(
      key: anchor.web3.PublicKey,
      version: ShadowDriveVersion
    ): Promise<ShadowDriveResponse> {
      let selectedAccount;
      
      // Fetch the selected account based on the version
      try {
        switch (version.toLocaleLowerCase()) {
          case "v1":
            selectedAccount = await this.program.account.storageAccount.fetch(key);
            break;
          case "v2":
            selectedAccount = await this.program.account.storageAccountV2.fetch(
              key
            );
            break;
        }
        
        // Find associated token addresses
        const ownerAta = await findAssociatedTokenAddress(
          selectedAccount.owner1,
          tokenMint
        );
        const emissionsAta = await findAssociatedTokenAddress(emissions, tokenMint);
        
        // Get stake account
        let stakeAccount = (await getStakeAccount(this.program, key))[0];
        let txn;
        
        // Create transaction based on the version
        switch (version.toLocaleLowerCase()) {
          case "v1":
            txn = await this.program.methods
              .makeAccountImmutable()
              .accounts({
                storageConfig: this.storageConfigPDA,
                storageAccount: key,
                stakeAccount,
                emissionsWallet: emissionsAta,
                owner: selectedAccount.owner1,
                uploader: uploader,
                ownerAta,
                tokenMint: tokenMint,
                systemProgram: anchor.web3.SystemProgram.programId,
                tokenProgram: TOKEN_PROGRAM_ID,
                associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
                rent: anchor.web3.SYSVAR_RENT_PUBKEY,
              })
              .transaction();
          case "v2":
            txn = await this.program.methods
              .makeAccountImmutable2()
              .accounts({
                storageConfig: this.storageConfigPDA,
                storageAccount: key,
                owner: selectedAccount.owner1,
                ownerAta,
                stakeAccount,
                uploader: uploader,
                emissionsWallet: emissionsAta,
                tokenMint: tokenMint,
                systemProgram: anchor.web3.SystemProgram.programId,
                tokenProgram: TOKEN_PROGRAM_ID,
                associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
                rent: anchor.web3.SYSVAR_RENT_PUBKEY,
              })
              .transaction();
            break;
        }
        
        // Set recent blockhash and fee payer
        txn.recentBlockhash = (
          await this.connection.getLatestBlockhash()
        ).blockhash;
        txn.feePayer = this.wallet.publicKey;
        let signedTx;
        let serializedTxn;
        
        // Sign and serialize the transaction
        if (!isBrowser) {
          await txn.partialSign(this.wallet.payer);
          serializedTxn = txn.serialize({ requireAllSignatures: false });
        } else {
          signedTx = await this.wallet.signTransaction(txn);
          serializedTxn = signedTx.serialize({ requireAllSignatures: false });
        }
        
        // Send the transaction to the server
        const makeImmutableResponse = await fetch(
          `${SHDW_DRIVE_ENDPOINT}/make-immutable`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              transaction: Buffer.from(serializedTxn.toJSON().data).toString(
                "base64"
              ),
            }),
          }
        );
        
        // Handle server response
        if (!makeImmutableResponse.ok) {
          return Promise.reject(
            new Error(`Server response status code: ${
              makeImmutableResponse.status
            } \n
    			Server response status message: ${(await makeImmutableResponse.json()).error}`)
          );
        }
        
        // Return the response JSON
        const responseJson = await makeImmutableResponse.json();
        return Promise.resolve(responseJson);
      } catch (e) {
        return Promise.reject(new Error(e));
      }
    }

    Rust

    shdwDrive v1.5 is no longer maintained. Please migrate to v2 and consult the new developer guide for instructions.

    Contents

    Install

    The Rust SDK is available on and Rust SDK

    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"

    You can find more examples on our

    Example - Upload Multiple Files to ShdwDrive Using Rust

    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.

    Methods

    add_immutable_storage

    Definition

    Adds storage capacity to the specified immutable StorageAccount. This will fail if the StorageAccount is not immutable.

    Parameters

    • storage_account_key - The public key of the immutable 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.

    Example of add_immutable_storage

    Response from add_immutable_storage

    add_storage

    Definition

    Adds storage capacity to the specified StorageAccount.

    Parameters

    • 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.

    Example of add_storage

    Response from add_storage

    cancel_delete_storage_account

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount that you want to unmark for deletion.

    Example of cancel_delete_storage_account

    Response from cancel_delete_storage_account

    claim_stake

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount that you want to claim excess stake from.

    Example of claim_stake

    Response from claim_stake

    create_storage_account

    Definition

    Creates a StorageAccount on the ShdwDrive. StorageAccounts can hold multiple files and are paid for using the SHDW token.

    Parameters

    • name - The name of the StorageAccount. Does not need to be unique.

    • size - The amount of storage the StorageAccount should be initialized with. When specifying size, only KB, MB, and GB storage units are currently supported.

    Example of create_storage_account

    An example use case for this method can be found in the same

    Response from create_storage_account

    delete_file

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount that contains the file.

    • url - The ShdwDrive url of the file you want to mark for deletion.

    Example of delete_file

    An example use case for this method can be found in the same

    delete_storage_account

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount that you want to mark for deletion.

    Response from delete_storage_account

    • 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.

    Example of delete_storage_account

    An example use case for this method can be found in the same on line 71.

    edit_file

    Definition

    Replace an existing file on the ShdwDrive with the given updated file.

    Parameters

    • storage_account_key - The public key of the StorageAccount that contains the file.

    • url - The ShdwDrive url of the file you want to replace.

    • data - The updated ShadowFile

    Example of edit_file

    Response from edit_file

    Examples found in

    File: examples/end_to_end.rs, Line 53

    get_object_data

    Retrieve object data

    get_storage_account

    Definition

    Returns the StorageAccount associated with the pubkey provided by a user.

    Parameters

    • key - The public key of the StorageAccount.

    Example of get_storage_account

    Response for V1 StorageAccount from get_storage_account

    Response for V2 StorageAccount from get_storage_account

    get_storage_accounts

    Definition

    Returns all StorageAccounts associated with the public key provided by a user.

    Parameters

    • owner - The public key that is the owner of all the returned StorageAccounts.

    Example of get_storage_accounts

    Response for V1 StorageAccount from get_storage_accounts

    Response for V2 StorageAccount from get_storage_accounts

    get_storage_account_size

    Definition

    This method is used to get the amount of storage currently used by a given storage account.

    Parameters

    • storage_account_key - The public key of the StorageAccount that owns the files.

    Example of get_storage_account_size

    Response from get_storage_account_size

    list_objects

    Definition

    Gets a list of all files associated with a StorageAccount. The output contains all of the file names as strings.

    Parameters

    • storage_account_key - The public key of the StorageAccount that owns the files.

    Example of list_objects

    Response from list_objects

    Note: The response is a vector containing all of the file names as strings.

    make_storage_immutable

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount that will be made immutable.

    Example of make_storage_immutable

    Response from make_storage_immutable

    migrate

    Definition

    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.

    Parameters

    • storage_account_key - The public key of the StorageAccount to be migrated.

    Example of migrate

    Response from migrate

    migrate_step_1

    Definition

    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.

    migrate_step_2

    Definition

    Second transaction step that migrates a v1 StorageAccount to v2. Consists of recreating the StorageAccount using the original pubkey, and deleting the intermediate account.

    new

    Definition

    Creates a new ShadowDriveClient from the given Signer and URL.

    Parameters

    • wallet - A Signer 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.

    Example of new

    Examples found in

    examples/end_to_end.rs (line 19)

    new_with_rpc

    Definition

    Creates a new ShadowDriveClient from the given Signer and RpcClient.

    Parameters

    • wallet - A Signer that for signs all transactions generated by the client. Typically this is a user’s keypair.

    • rpc_client - A Solana RpcClient that handles sending transactions and reading accounts from the blockchain. Providing the RpcClient allows customization of timeout and commitment level.

    Example of new_with_rpc

    redeem_rent

    Definition

    Reclaims the Solana rent from any on-chain file accounts. Older versions of the ShdwDrive used to create accounts for uploaded files.

    Parameters

    • 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.

    Example of redeem_rent

    Response from redeem_rent

    reduce_storage

    Definition

    Reduces the amount of total storage available for the given StorageAccount.

    Parameters

    • storage_account_key - The public key of the StorageAccount whose storage will be reduced.

    • size - The amount of storage you want to remove. E.g if you have an existing StorageAccount 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.

    Example of reduce_storage

    Response from reduce_storage

    refresh_stake

    Definition

    This method is used to update your storage account's stake amount. It is required to call this method after calling the `` method in order for your stage account to update properly.

    Parameters

    • storage_account_key: PublicKey - Publickey of the Storage Account

    Example of refresh_stake

    Response from refresh_stake

    store_files

    Definition

    Stores files in the specified StorageAccount.

    Parameters

    • storage_account_key - The public key of the StorageAccount.

    • data - Vector of ShadowFile objects representing the files that will be stored.

    Example of store_files

    Response from store_files

    top_up

    Definition

    This method is used to top up a storage account's $SHDW balance to cover any necessary fees, like mutable storage fees which are collected every epoch. It is necessary to call the `` method after this.

    Parameters

    • key: PublicKey - Publickey of the Storage Account

    • amount: u64- Amount of $SHDW to transfer to the stake account

    Example of top_up

    Response from top_up

    Example - ShdwDrive Client: Creating and Managing Storage Accounts using Rust

    Example - Cancel Storage Account Deletion using rust

    Example - Claim Stake using rust

    Example - Uploading and Deleting Files with rust SDK

    Example - Delete Storage Account using rust

    Example - Tests

    Example - Creating and Migrating a Storage Account using ShadowDriveClient in Rust

    Example - Redeem Rent for Storage using Rust

    Example - Uploading Multiple Files to a storage account in rust

    claim_stake

  • create_storage_account

  • delete_file

  • delete_storage_account

  • edit_file

  • get_object_data

  • get_storage_account

  • get_storage_account_size (new)

  • get_storage_accounts

  • list_objects

  • make_storage_immutable (updated)

  • migrate

  • migrate_step_1

  • migrate_step_2

  • new

  • new_with_rpc

  • redeem_rent

  • reduce_storage (updated)

  • refresh_stake (new)

  • store_files

  • top_up (new)

  • .
    Install
    Example
    Methods
    add_immutable_storage
    add_storage
    cancel_delete_storage_account
    More Examples
    crates.io
    Github
    Github
    github repository
    github repository
    github repository
    repository
    repository
    topUp
    refresh_stake
    // 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);
    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>,
    }
    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>,
    }
    let cancel_delete_storage_account_response = shdw_drive_client
        .cancel_delete_storage_account(&storage_account_key)
        .await?;
    {
        txid: String,
    }
    let claim_stake_response = shdw_drive_client
        .claim_stake(&storage_account_key)
        .await?;
    {
        txid: String,
    }
    // 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>,
    }
    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");
        }
    }
    let delete_storage_account_response = shdw_drive_client
        .delete_storage_account(&storage_account_key)
        .await?;
    let edit_file_response = shdw_drive_client
        .edit_file(&storage_account_key, url, file)
        .await?;
    {
        finalized_location: String,
        error: String,
    }
    // 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");
        }    
    }
    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,
    }
    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,
    }
    let storage_account_size = shdw_drive_client
        .get_stroage_account_size(&storage_account_key)
        .await?;
    {
        storage_used: u64;
        error: Option<String>;
    }
    let files = shdw_drive_client
        .list_objects(&storage_account_key)
        .await?;
    let make_immutable_response = shdw_drive_client  
        .make_storage_immutable(&storage_account_key)  
        .await?;  
    {
        message: String,
        transaction_signature: String,
        error: Option<String>,
    }
    let migrate_response = shdw_drive_client
        .migrate(&storage_account_key)
        .await?;
    {
        txid: String,
    }
    use solana_sdk::signer::keypair::Keypair;    
    
    let wallet = Keypair::generate();
    let shdw_drive = ShadowDriveClient::new(wallet, "https://ssc-dao.genesysgo.net");
    // 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");
    }
    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);
    let redeem_rent_response = shdw_drive_client
        .redeem_rent(&storage_account_key, &file_account_key)
        .await?;
    {
      message: String,
      transaction_signature: String,
      error: Option<String>,
    }
    let reduce_storage_response = shdw_drive_client
        .reduce_storage(&storage_account_key, reduced_bytes)
        .await?;
    {
      message: String,
      transaction_signature: String,
      error: Option<String>,
    }
    let refresh_stake = shdw_drive_client
        .refresh_stake(&storage_account_key)
        .await?;
    {
        txid: string;
    }
    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 store_files_response = shdw_drive_client
        .store_files(&storage_account_key, files)
        .await?;
    {
      finalized_locations: Array<string>;
      message: string;
      upload_errors: Array<UploadError>;
    }
    let top_up_amount: u64 = 1000;
    let top_up = shdw_drive_client
        .top_up(&storage_account_key, top_up_amount)
        .await?;
    let refresh_stake = shdw_drive_client
        .refresh_stake(&storage_account_key)
        .await?;
    {
        txid: string;
    }
    use byte_unit::Byte;
    use shadow_drive_rust::{
        models::storage_acct::StorageAcct, ShadowDriveClient, StorageAccountVersion,
    };
    use solana_sdk::{
        pubkey::Pubkey,
        signer::{keypair::read_keypair_file, Signer},
    };
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "/Users/dboures/.config/solana/id.json";
    
    // Main function demonstrating the usage of ShdwDrive Rust client
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // // 1.
        // create_storage_accounts(shdw_drive_client).await;
    
        // // 2.
        // let v1_pubkey = Pubkey::from_str("J4RJYandDDKxyF6V1HAdShDSbMXk78izZ2yEksqyvGmo").unwrap();
        let v2_pubkey = Pubkey::from_str("9dXUV4BEKWohSRDn4cy5G7JkhUDWoSUGGwJngrSg453r").unwrap();
    
        // make_storage_immutable(&shdw_drive_client, &v1_pubkey).await;
        // make_storage_immutable(&shdw_drive_client, &v2_pubkey).await;
    
        // // 3.
        // add_immutable_storage_test(&shdw_drive_client, &v1_pubkey).await;
        add_immutable_storage_test(&shdw_drive_client, &v2_pubkey).await;
    }
    
    // Function to create storage accounts with specified version and size
    async fn create_storage_accounts<T: Signer>(shdw_drive_client: ShadowDriveClient<T>) {
        let result_v1 = shdw_drive_client
            .create_storage_account(
                "shdw-drive-1.5-test-v1",
                Byte::from_str("1MB").expect("invalid byte string"),
                StorageAccountVersion::v1(),
            )
            .await
            .expect("error creating storage account");
            
        // Create a storage account with version 2
        let result_v2 = shdw_drive_client
            .create_storage_account(
                "shdw-drive-1.5-test-v2",
                Byte::from_str("1MB").expect("invalid byte string"),
                StorageAccountVersion::v2(),
            )
            .await
            .expect("error creating storage account");
    
        println!("v1: {:?}", result_v1);
        println!("v2: {:?}", result_v2);
    }
    
    // Function to make a storage account immutable
    async fn make_storage_immutable<T: Signer>(
        shdw_drive_client: &ShadowDriveClient<T>,
        storage_account_key: &Pubkey,
    ) {
        let storage_account = shdw_drive_client
            .get_storage_account(storage_account_key)
            .await
            .expect("failed to get storage account");
        match storage_account {
            StorageAcct::V1(storage_account) => println!("account: {:?}", storage_account),
            StorageAcct::V2(storage_account) => println!("account: {:?}", storage_account),
        }
    
        // Make the storage account immutable
        let make_immutable_response = shdw_drive_client
            .make_storage_immutable(&storage_account_key)
            .await
            .expect("failed to make storage immutable");
    
        println!("response: {:?}", make_immutable_response);
    
        let storage_account = shdw_drive_client
            .get_storage_account(&storage_account_key)
            .await
            .expect("failed to get storage account");
        match storage_account {
            StorageAcct::V1(storage_account) => println!("account: {:?}", storage_account),
            StorageAcct::V2(storage_account) => println!("account: {:?}", storage_account),
        }
    }
    
    // Function to add immutable storage to a storage account
    async fn add_immutable_storage_test<T: Signer>(
        shdw_drive_client: &ShadowDriveClient<T>,
        storage_account_key: &Pubkey,
    ) {
        let storage_account = shdw_drive_client
            .get_storage_account(&storage_account_key)
            .await
            .expect("failed to get storage account");
    
        match storage_account {
            StorageAcct::V1(storage_account) => {
                println!("old size: {:?}", storage_account.reserved_bytes)
            }
            StorageAcct::V2(storage_account) => {
                println!("old size: {:?}", storage_account.reserved_bytes)
            }
        }
    
        // Add immutable storage to the account
        let add_immutable_storage_response = shdw_drive_client
            .add_immutable_storage(
                storage_account_key,
                Byte::from_str("1MB").expect("invalid byte string"),
            )
            .await
            .expect("error adding storage");
    
        println!("response: {:?}", add_immutable_storage_response);
    
        let storage_account = shdw_drive_client
            .get_storage_account(&storage_account_key)
            .await
            .expect("failed to get storage account");
    
        match storage_account {
            StorageAcct::V1(storage_account) => {
                println!("new size: {:?}", storage_account.reserved_bytes)
            }
            StorageAcct::V2(storage_account) => {
                println!("new size: {:?}", storage_account.reserved_bytes)
            }
        }
    }
    // Import necessary libraries and modules
    use shadow_drive_rust::ShadowDriveClient;
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    // Define the path to the keypair file
    const KEYPAIR_PATH: &str = "keypair.json";
    
    // Main function with async support
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
        let storage_account_key =
            Pubkey::from_str("GHSNTDyMmay7xDjBNd9dqoHTGD3neioLk5VJg2q3fJqr").unwrap();
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // Send a request to cancel the deletion of the storage account
        let response = shdw_drive_client
            .cancel_delete_storage_account(&storage_account_key)
            .await
            .expect("failed to cancel storage account deletion");
    
        println!("Unmark delete storage account complete {:?}", response);
    }
    use shadow_drive_rust::ShadowDriveClient;
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    /// This example doesn't quite work.
    /// claim_stake is used to redeem SHDW after you reduce the storage amount of an account
    /// In order to successfully claim_stake, the user needs to wait an epoch after reducing storage
    /// Trying to claim_stake in the same epoch as a reduction will result in
    /// "custom program error: 0x1775"
    /// "Error Code: ClaimingStakeTooSoon"
    
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
        let storage_account_key =
            Pubkey::from_str("GHSNTDyMmay7xDjBNd9dqoHTGD3neioLk5VJg2q3fJqr").unwrap();
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        let url = String::from(
            "https://shdw-drive.genesysgo.net/B7Qk2omAvchkePhzHubCVQuVpZHcieqPQCwFxeeBZGuT/hey.txt",
        );
    
        // reduce storage
    
        // let reduce_storage_response = shdw_drive_client
        //     .reduce_storage(
        //         storage_account_key,
        //         Byte::from_str("100KB").expect("invalid byte string"),
        //     )
        //     .await
        //     .expect("error adding storage");
    
        // println!("txn id: {:?}", reduce_storage_response.txid);
    
        // WAIT AN EPOCH
    
        // claim stake
        // let claim_stake_response = shdw_drive_client
        //     .claim_stake(storage_account_key)
        //     .await
        //     .expect("failed to claim stake");
    
        // println!(
        //     "Claim stake complete {:?}",
        //     claim_stake_response
        // );
    }
    // Import necessary modules and types
    use shadow_drive_rust::{models::ShadowFile, ShadowDriveClient};
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    // Main function
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    
        let v1_pubkey = Pubkey::from_str("GSvvRguVTtSayF5zLQPLVTJQHQ6Fqu1Z3HSRMP8AHY9K").unwrap();
        let v2_pubkey = Pubkey::from_str("2cvgcqfmMg9ioFtNf57ZqCNbuWDfB8ZSzromLS8Kkb7q").unwrap();
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // Upload file for v1_pubkey
        let v1_upload_reponse = shdw_drive_client
            .store_files(
                &v1_pubkey,
                vec![ShadowFile::file(
                    String::from("example.png"),
                    "multiple_uploads/0.txt",
                )],
            )
            .await
            .expect("failed to upload v1 file");
        println!("Upload complete {:?}", v1_upload_reponse);
    
        // Upload file for v2_pubkey
        let v2_upload_reponse = shdw_drive_client
            .store_files(
                &v2_pubkey,
                vec![ShadowFile::file(
                    String::from("example.png"),
                    "multiple_uploads/0.txt",
                )],
            )
            .await
            .expect("failed to upload v2 file");
    
        println!("Upload complete {:?}", v2_upload_reponse);
    
        let v1_url = String::from(
            "https://shdw-drive.genesysgo.net/GSvvRguVTtSayF5zLQPLVTJQHQ6Fqu1Z3HSRMP8AHY9K/example.png",
        );
        let v2_url = String::from(
            "https://shdw-drive.genesysgo.net/2cvgcqfmMg9ioFtNf57ZqCNbuWDfB8ZSzromLS8Kkb7q/example.png",
        );
    
        //delete file
        // Delete file for v1_pubkey
        let v1_delete_file_response = shdw_drive_client
            .delete_file(&v1_pubkey, v1_url)
            .await
            .expect("failed to delete file");
        println!("Delete file complete {:?}", v1_delete_file_response);
    
        // Delete file for v2_pubkey
        let v2_delete_file_response = shdw_drive_client
            .delete_file(&v2_pubkey, v2_url)
            .await
            .expect("failed to delete file");
        println!("Delete file complete {:?}", v2_delete_file_response);
    }
    use shadow_drive_rust::ShadowDriveClient;
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
        let storage_account_key =
            Pubkey::from_str("9VndNFwL7cVTshY2x5GAjKQusRCAsDU4zynYg76xTguo").unwrap();
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // Request storage account deletion
        let response = shdw_drive_client
            .delete_storage_account(&storage_account_key)
            .await
            .expect("failed to request storage account deletion");
    
        println!("Delete Storage Account complete {:?}", response);
    }
    use byte_unit::Byte;
    use shadow_drive_rust::{models::ShadowFile, ShadowDriveClient, StorageAccountVersion};
    use solana_sdk::{
        pubkey,
        pubkey::Pubkey,
        signer::{keypair::read_keypair_file, Signer},
    };
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
        // let pubkey = keypair.pubkey();
        // let (storage_account_key, _) =
        //     shadow_drive_rust::derived_addresses::storage_account(&pubkey, 0);
    
        let storage_account_key = pubkey!("G6nE9EbNgSDcvUvs67enP2Jba3exgLyStgsg8S7n9StS");
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // get_storage_accounts_test(shdw_drive_client, &pubkey).await
    
        // create_storage_account_v2_test(shdw_drive_client).await
        upload_file_test(shdw_drive_client, &storage_account_key).await
    }
    
    async fn get_storage_accounts_test<T: Signer>(
        shdw_drive_client: ShadowDriveClient<T>,
        pubkey: &Pubkey,
    ) {
        let storage_accounts = shdw_drive_client
            .get_storage_accounts(pubkey)
            .await
            .expect("failed to get storage account");
        println!("{:?}", storage_accounts);
    }
    
    async fn create_storage_account_v2_test<T: Signer>(shdw_drive_client: ShadowDriveClient<T>) {
        let result = shdw_drive_client
            .create_storage_account(
                "shdw-drive-1.5-test",
                Byte::from_str("10MB").expect("invalid byte string"),
                StorageAccountVersion::v2(),
            )
            .await
            .expect("error creating storage account");
        println!("{:?}", result);
    }
    
    // async fn get_object_data_test<T: Signer>(
    //     shdw_drive_client: ShadowDriveClient<T>,
    //     location: &str,
    // ) {
    //     let result = shdw_drive_client
    //         .get_object_data(location)
    //         .await
    //         .expect("error getting object data");
    //     println!("{:?}", result);
    // }
    
    // async fn list_objects_test<T: Signer>(
    //     shdw_drive_client: ShadowDriveClient<T>,
    //     storage_account_key: &Pubkey,
    // ) {
    //     let objects = shdw_drive_client
    //         .list_objects(storage_account_key)
    //         .await
    //         .expect("failed to list objects");
    
    //     println!("objects {:?}", objects);
    // }
    
    // async fn make_storage_immutable_test<T: Signer>(
    //     shdw_drive_client: ShadowDriveClient<T>,
    //     storage_account_key: &Pubkey,
    // ) {
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    //     println!(
    //         "identifier: {:?}; immutable: {:?}",
    //         storage_account.identifier, storage_account.immutable
    //     );
    
    //     let make_immutable_response = shdw_drive_client
    //         .make_storage_immutable(&storage_account_key)
    //         .await
    //         .expect("failed to make storage immutable");
    
    //     println!("txn id: {:?}", make_immutable_response.txid);
    
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(&storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    //     println!(
    //         "identifier: {:?}; immutable: {:?}",
    //         storage_account.identifier, storage_account.immutable
    //     );
    // }
    
    // async fn add_storage_test<T: Signer>(
    //     shdw_drive_client: &ShadowDriveClient<T>,
    //     storage_account_key: &Pubkey,
    // ) {
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(&storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    
    //     let add_storage_response = shdw_drive_client
    //         .add_storage(
    //             storage_account_key,
    //             Byte::from_str("10MB").expect("invalid byte string"),
    //         )
    //         .await
    //         .expect("error adding storage");
    
    //     println!("txn id: {:?}", add_storage_response.txid);
    
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(&storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    
    //     println!("new size: {:?}", storage_account.storage);
    // }
    
    // async fn reduce_storage_test<T: Signer>(
    //     shdw_drive_client: ShadowDriveClient<T>,
    //     storage_account_key: &Pubkey,
    // ) {
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    
    //     println!("previous size: {:?}", storage_account.storage);
    
    //     let add_storage_response = shdw_drive_client
    //         .reduce_storage(
    //             storage_account_key,
    //             Byte::from_str("10MB").expect("invalid byte string"),
    //         )
    //         .await
    //         .expect("error adding storage");
    
    //     println!("txn id: {:?}", add_storage_response.txid);
    
    //     let storage_account = shdw_drive_client
    //         .get_storage_account(storage_account_key)
    //         .await
    //         .expect("failed to get storage account");
    
    //     println!("new size: {:?}", storage_account.storage);
    // }
    
    async fn upload_file_test<T: Signer>(
        shdw_drive_client: ShadowDriveClient<T>,
        storage_account_key: &Pubkey,
    ) {
        let upload_reponse = shdw_drive_client
            .store_files(
                storage_account_key,
                vec![ShadowFile::file(String::from("example.png"), "example.png")],
            )
            .await
            .expect("failed to upload file");
    
        println!("Upload complete {:?}", upload_reponse);
    }
    use byte_unit::Byte;
    use shadow_drive_rust::{ShadowDriveClient, StorageAccountVersion};
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    // Main function to demonstrate creating and migrating a storage account 
    // using ShadowDriveClient
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        // create V1 storage account
        let v1_response = shdw_drive_client
            .create_storage_account(
                "1.5-test",
                Byte::from_str("1MB").expect("invalid byte string"),
                StorageAccountVersion::v1(),
            )
            .await
            .expect("error creating storage account");
    
        println!("v1: {:?} \n", v1_response);
    
        let key_string: String = v1_response.shdw_bucket.unwrap();
        let v1_pubkey: Pubkey = Pubkey::from_str(&key_string).unwrap();
    
        // can migrate all at once
        let migrate = shdw_drive_client
            .migrate(&v1_pubkey)
            .await
            .expect("failed to migrate");
        println!("Migrated {:?} \n", migrate);
    
        // alternatively can split migration into 2 steps (boths steps are exposed)
    
        // // step 1
        // let migrate_step_1 = shdw_drive_client
        //     .migrate_step_1(&v1_pubkey)
        //     .await
        //     .expect("failed to migrate v1 step 1");
        // println!("Step 1 complete {:?} \n", migrate_step_1);
    
        // // step 2
        // let migrate_step_2 = shdw_drive_client
        //     .migrate_step_2(&v1_pubkey)
        //     .await
        //     .expect("failed to migrate v1 step 2");
        // println!("Step 2 complete {:?} \n", migrate_step_2);
    }
    use shadow_drive_rust::ShadowDriveClient;
    use solana_sdk::{pubkey::Pubkey, signer::keypair::read_keypair_file};
    use std::str::FromStr;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    #[tokio::main]
    async fn main() {
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
    
        //create shdw drive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        let storage_account_key =
            Pubkey::from_str("D7Qk2omAvchkePhzHubCVQuVpZHcieqPQCwFxeeBZGuT").unwrap();
    
        let file_account_key =
            Pubkey::from_str("B41kFXqFkDhY7kHbMhEk17bP2w7QLUYU9X5tRhDLttnJ").unwrap();
    
        let redeem_rent_response = shdw_drive_client
            .redeem_rent(&storage_account_key, &file_account_key)
            .await
            .expect("failed to redeem_storage");
        println!("Redeemed {:?} \n", redeem_rent_response);
    }
    use byte_unit::Byte;
    use futures::TryStreamExt;
    use shadow_drive_rust::{models::ShadowFile, ShadowDriveClient, StorageAccountVersion};
    use solana_sdk::signer::{keypair::read_keypair_file, Signer};
    use tokio_stream::StreamExt;
    
    const KEYPAIR_PATH: &str = "keypair.json";
    
    // Main function for uploading multiple files to a ShdwDrive storage account
    #[tokio::main]
    async fn main() {
        tracing_subscriber::fmt()
            .with_env_filter("off,shadow_drive_rust=debug")
            .init();
    
        //load keypair from file
        let keypair = read_keypair_file(KEYPAIR_PATH).expect("failed to load keypair at path");
        let pubkey = keypair.pubkey();
        let (storage_account_key, _) =
            shadow_drive_rust::derived_addresses::storage_account(&pubkey, 21);
    
        //create ShdwDrive client
        let shdw_drive_client = ShadowDriveClient::new(keypair, "https://ssc-dao.genesysgo.net");
    
        //ensure storage account exists
        if let Err(_) = shdw_drive_client
            .get_storage_account(&storage_account_key)
            .await
        {
            println!("Error finding storage account, assuming it's not created yet");
            shdw_drive_client
                .create_storage_account(
                    "shadow-drive-rust-test-2",
                    Byte::from_str("1MB").expect("failed to parse byte string"),
                    StorageAccountVersion::v2(),
                )
                .await
                .expect("failed to create storage account");
        }
    
        // Read files from "multiple_uploads" directory
        let dir = tokio::fs::read_dir("multiple_uploads")
            .await
            .expect("failed to read multiple uploads dir");
    
        // Create ShadowFile objects for each file in the directory
        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 object with byte content
        files.push(ShadowFile::bytes(
            String::from("buf.txt"),
            &b"this is a buf test"[..],
        ));
    
        // Upload files to the storage account
        let upload_results = shdw_drive_client
            .store_files(&storage_account_key, files)
            .await
            .expect("failed to upload files");
    
        println!("upload results: {:#?}", upload_results);
    }