JavaScript
Let's scaffold a React app and add our dependencies
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
Use the Solana docs and examples here if you need help. We're going to focus on Shadow Drive 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:
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>
);
};
Let's start by instantiating the Shadow Drive connection class object. This will have all Shadow Drive 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 Shadow Drives upon wallet connection. This can be done with the
useEffect
React hook.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>
This can be done with a NodeJS + TypeScript program as well.
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();
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 accountsize
: The size of your storage accounts with a human readable ending containingKB
,MB
, orGB
version
: can be eitherv1
orv2
. Note -v1
is completely deprecated and you shuold only usev2
moving forward.
//create account
const newAcct = await drive.createStorageAccount("myDemoBucket", "10MB", "v2");
console.log(newAcct);
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.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());
Full Response:
[
{
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'
}
}
]
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.
const acct = await drive.getStorageAccount(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
console.log(acct);
Full Response:
{
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'
}
The
uploadFile
method requires two parameters:key
: A PublicKey object representing the public key of the Shadow Storage Accountdata
: A file of either theFile
object type orShadowFile
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:
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>Shadow Drive 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>
);
}
And a NodeJS + TypeScript implementation would look like:
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);
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.
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 keyurl
: the current URL of the file to be deletedversion
: can be eitherv1
orv2
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);

The editFile method is a combo of
uploadFile
and deleteFile
. Let's look at the params:key
: the Public Key of the storage accounturl
: the URL of the file that is being replaceddata
: the file that is replacing the current file. It must have the exact same filename and extension, and it must be aFile
orShadowFile
objectversion
: eitherv1
orv2
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);
This is a simple implementation that only requires a public key to get the file names of a storage account.
const acctPubKey = new anchor.web3.PublicKey(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
const listItems = await drive.listObjects(acctPubKey);
console.log(listItems);
And the response payload:
{ keys: [ 'index.html' ] }
This is a method to simply increase the storage limit of a storage account. It requires three params:
key
: storage account public keysize
: amount to increase by, must end withKB
,MB
, orGB
version
: storage account version, must bev1
orv2
const accts = await drive.getStorageAccounts("v2");
let acctPubKey = new anchor.web3.PublicKey(accts[1].publicKey);
const addStgResp = await drive.addStorage(acctPubKey, "10MB", "v2");
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.
const acctPubKey = new anchor.web3.PublicKey(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
const shrinkAcct = await drive.reduceStorage(acctPubKey, "10MB", "v2");
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.
const acctPubKey = new anchor.web3.PublicKey(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
const claimStake = await drive.claimStake(acctPubKey, "v2");
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.
const acctPubKey = new anchor.web3.PublicKey(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
const delAcct = await drive.deleteStorageAccount(acctPubKey, "v2");
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.
const acctPubKey = new anchor.web3.PublicKey(
"EY8ZktbRmecPLfopBxJfNBGUPT1LMqZmDFVcWeMTGPcN"
);
const cancelDelStg = await drive.cancelDeleteStorageAccount(acctPubKey, "v2");
This method is used to create a new instance of the ShadowDrive class. It accepts a web3 connection object and a web3 wallet. It returns an instance of the ShadowDrive class.
connection
:Connection
- initialized web3 connection objectwallet
:any
- Web3 wallet
It returns an instance of the ShadowDrive class.
Example
Explanations
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();
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.key
:PublicKey
- Public Key of the existing storage to increase size onsize
: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.version
:ShadowDriveVersion
- ShadowDrive version (v1 or v2)
Confirmed transaction ID
{
message: string;
transaction_signature?: string
}
Example
Explanations
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
Implementation of ShadowDrive.cancelDeleteStorageAccount defined in index.ts:135 This method is used to cancel a delete request for a Storage Account on ShadowDrive. It accepts a Public Key of the Storage Account and the Shadow Drive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the undelete request.
key
:PublicKey
- Publickey
Confirmed transaction ID
Example
Explanations
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 Shadow Drive 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");
This method is used to request a Stake on ShadowDrive. It accepts a PublicKey of the Storage Account and the ShadowDrive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the claimStake request.
key
:PublicKey
- Publickey of Storage Accountversion
: `ShadowDrive
Confirmed transaction ID
Example
Explanations
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");
Implementation of ShadowDrive.createStorageAccount defined in index.ts:120 This method is used to create a new Storage Account on ShadowDrive. It accepts the name of the Storage Account, the size of the requested Storage Account, and the ShadowDrive 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.
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.version
:ShadowDriveVersion
- ShadowDrive version(v1 or v2)owner2
(optional):PublicKey
- Optional secondary owner for the storage account.
{
"shdw_bucket": String,
"transaction_signature": String
}
Example
Explanations
//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);
This method is used to delete a file on ShadowDrive. It accepts a Public Key of your Storage Account, the Shadow Drive URL of the file you are requesting to delete and the ShadowDrive version (v1 or v2). It returns a Promise containing the confirmed transaction ID of the delete request.
key
:PublicKey
- Publickey of Storage Accounturl
:string
- Shadow Drive URL of the file you are requesting to delete.version
: `ShadowDrive- Version` - ShadowDrive version (v1 or v2)
{
"message": String,
"error": String or not passed if no error
}
Example
Explanations
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);
Implementation of ShadowDrive.deleteStorageAccount defined in index.ts:124 This method is used to delete a Storage Account on ShadowDrive. It accepts a Public Key of the Storage Account and the Shadow Drive version (v1 or v2). It returns a Promise<{ txid: string }> containing the confirmed transaction ID of the delete request.
key
:PublicKey
- Publickey of a Storage Accountversion
:ShadowDriveVersion
- ShadowDrive version (v1 or v2)
Confirmed transaction ID
Example
Explanations
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");
This method is used to edit a file on ShadowDrive. It accepts a Public Key of your Storage Account, the URL of the existing file, the File or ShadowFile object, and the ShadowDrive version (v1 or v2). It returns a Promise containing the file location and the transaction signature.
key
:PublicKey
- Publickey of Storage Accounturl
:string
- URL of existing filedata
:File | ShadowFile
- File or ShadowFile object, file extensions should be included in the name property of ShadowFiles.version
:ShadowDriveVersion
- ShadowDrive version (v1 or v2)
{
finalized_location: string;
}
Example
Explanations
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);
This method is used to get the details of a Storage Account on ShadowDrive. It accepts a Public Key of the Storage Account and returns a Promise containing the Storage Account details.
key
:PublicKey
- Publickey of a Storage Account
{
storage_account: PublicKey;
reserved_bytes: number;
current_usage: number;
immutable: boolean;
to_be_deleted: boolean;
delete_request_epoch: number;
owner1: PublicKey;