Manage Files and Buckets¶
This guide explains how to manage your storage resources on DataHaven using the StorageHub SDK. You will learn how to fetch all the buckets you have stored within a specific MSP, fetch all the files within a specific bucket in a specific MSP, request the removal of a file from the network, and delete buckets. It's important to periodically review and clean up unused data to avoid unnecessary costs, as buckets and files incur ongoing storage fees.
Prerequisites¶
- Node.js v22+ installed
-
A TypeScript project
Need a starter project?
If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:
-
Create a new project folder by executing the following command in the terminal:
-
Initialize a
package.jsonfile using the correct command for your package manager: -
Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:
-
Create a
tsconfig.jsonfile in the root of your project and paste the following configuration: -
Initialize the
srcdirectory:
-
-
A file uploaded to DataHaven, along with the file key
Initialize the Script Entry Point¶
First, create an index.ts file if you haven't already. Its run method will orchestrate all the logic in this guide, and you’ll replace the labelled placeholders with real code step by step. By now, your services folder (including the MSP and client helper services) should already be created. If not, see the Get Started guide.
The index.ts snippet below also imports bucketOperations.ts and fileOperations.ts, which are not in your project yet—that's expected, as you'll create them later in this guide.
Add the following code to your index.ts file:
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
// --- Data fetching logic ---
// **PLACEHOLDER FOR STEP 2: GET YOUR BUCKETS**
// **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
// --- Data deleting logic ---
// **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Authenticate¶
Before any file operations, authenticate with the MSP. The authenticateUser helper signs a SIWE message and returns a session token that authorizes your uploads, updates, and deletions. Add the following code to use the authenticateUser helper method you've already implemented in mspService.ts:
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// --- Data fetching logic ---
// **PLACEHOLDER FOR STEP 2: GET YOUR BUCKETS**
// **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
// --- Data deleting logic ---
// **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Get Buckets From MSP¶
To fetch your buckets stored in a specific MSP, create a helper method called getBucketsFromMSP in a separate bucketOperations.ts file and then update the index.ts file accordingly, to execute that logic.
Add Method to Get Buckets From MSP¶
-
Create a new folder called
operationswithin thesrcfolder (at the same level as theservicesfolder) like so: -
Create a new file within the
operationsfolder calledbucketOperations.ts. -
Add the following code:
bucketOperations.tsimport { Bucket, FileListResponse } from '@storagehub-sdk/msp-client'; import { storageHubClient, publicClient } from '../services/clientService.js'; import { mspClient } from '../services/mspService.js'; export async function getBucketsFromMSP(): Promise<Bucket[]> { const buckets: Bucket[] = await mspClient.buckets.listBuckets(); return buckets; } // Add other helper methods here
Call the Get Buckets From MSP Helper Method¶
Update index.ts with the following code to trigger the getBucketsFromMSP helper method you just implemented:
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// --- Data fetching logic ---
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
// **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
// --- Data deleting logic ---
// **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
If you run the script with the code above, the full response should look like this:
Buckets in MSP: [
{
bucketId: '0xcfc81ed6cea71b005b3c4d08f15df08807e1545167a5e70c3b512fac3338ff87',
name: 'bucket-003',
root: '0x0000000000000000000000000000000000000000000000000000000000000000',
isPublic: true,
sizeBytes: 0,
valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
fileCount: 0
},
{
bucketId: '0xdd2148ff63c15826ab42953a9d214770e6c2a73b22b83d28819a1777ab9d1322',
name: 'bucket-004',
root: '0x6b3de909a86212096a7d7b51253667e15f05170519c4c6029166af4e49378b23',
isPublic: true,
sizeBytes: 0,
valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
fileCount: 0
},
{
bucketId: '0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b',
name: 'bucket-005',
root: '0x0000000000000000000000000000000000000000000000000000000000000000',
isPublic: true,
sizeBytes: 0,
valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
fileCount: 0
}
]
Get Bucket Files From MSP¶
To fetch your files from a specific bucket stored in a specific MSP, create a helper method called getBucketFilesFromMSP in a separate fileOperations.ts file and then update the index.ts file accordingly, to execute that logic.
Add Method to Get Bucket Files From MSP¶
-
Create a new file within the
operationsfolder calledfileOperations.ts. -
Add the following code:
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
import { FileInfo } from '@storagehub-sdk/core';
import { FileListResponse } from '@storagehub-sdk/msp-client';
export async function getBucketFilesFromMSP(
bucketId: string,
): Promise<FileListResponse> {
const files: FileListResponse = await mspClient.buckets.getFiles(bucketId);
return files;
}
// Add other helper methods here
Call the Get Bucket Files from MSP Helper Method¶
Update index.ts with the following code to trigger the getBucketFilesFromMSP helper method you just implemented:
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// --- Data fetching logic ---
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
// --- Data deleting logic ---
// **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
If you run the script with the code above, the response should look like this:
Files in bucket with ID 0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b:
{
"bucketId": "0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b",
"files": [
{
"name": "/",
"children": [
{
"name": "helloworld.txt",
"type": "file",
"sizeBytes": 18,
"fileKey": "0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85",
"status": "ready",
"uploadedAt": "2026-01-14T14:21:00.910Z"
}
]
}
]
}
Request File Deletion¶
To request file deletion, create a helper method called requestDeleteFile in your fileOperations.ts file and then update the index.ts file accordingly, to execute that logic. First, fetch the file’s metadata from the MSP and then submit a deletion request using the StorageHub SDK.
It’s important to note that files are not removed instantly. When a deletion request succeeds, the file is marked for deletion on-chain, but both the MSP and all BSPs storing it still have it in their Merkle Patricia Forests until they pass the mandatory storage proof challenge. After that, the runtime automatically updates the Merkle Patricia Forest roots to remove the file.
Add Method to Request File Deletion¶
Add the following code:
export async function requestDeleteFile(
bucketId: string,
fileKey: string,
): Promise<boolean> {
// Get file info before deletion
const fileInfo: FileInfo = await mspClient.files.getFileInfo(
bucketId,
fileKey,
);
console.log('File info:', fileInfo);
// Request file deletion
const txHashRequestDeleteFile: `0x${string}` =
await storageHubClient.requestDeleteFile(fileInfo);
console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);
// Wait for delete file transaction receipt
const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
{
hash: txHashRequestDeleteFile,
},
);
console.log('requestDeleteFile() txReceipt:', receiptRequestDeleteFile);
if (receiptRequestDeleteFile.status !== 'success') {
throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
}
console.log(
`File with key ${fileKey} deleted successfully from bucket ${bucketId}`,
);
return true;
}
Call the Request File Deletion Helper Method¶
Update index.ts with the following code to trigger the requestDeleteFile helper method you just implemented:
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey,
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful,
);
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// --- Data fetching logic ---
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
// --- Data deleting logic ---
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey,
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful,
);
// **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Note
After a file is uploaded to a Main Storage Provider (MSP), the network allows a 10-minute window for Backup Storage Providers (BSPs) to replicate the file to the required count. Within this time window, the deletion of a file cannot be requested. If the replication target is not met within this window, the request transitions to expired even though the upload to the MSP succeeded.
If you run the script with the code above, the full response should look like this:
File info: {
fileKey: '0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85',
fingerprint: '0x1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
bucketId: '0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b',
location: 'helloworld.txt',
size: 18,
isPublic: true,
uploadedAt: 2025-11-05T14:19:55.911Z,
status: 'ready'
}
requestDeleteFile() txHash: 0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c
requestDeleteFile() txReceipt: {
transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
transactionIndex: 0,
blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
to: '0x0000000000000000000000000000000000000404',
blockNumber: 115472n,
cumulativeGasUsed: 32400n,
gasUsed: 32400n,
contractAddress: null,
logs: [
{
address: '0x0000000000000000000000000000000000000404',
topics: [Array],
data: '0x',
blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
blockNumber: 115472n,
transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
transactionIndex: 0,
logIndex: 0,
transactionLogIndex: '0x0',
removed: false
}
],
logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000004000000000200000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000800000004000000080000000000000000000000000000000000000010000000000000000000000000000080000',
status: 'success',
effectiveGasPrice: 1000000000n,
type: 'legacy'
}
File with key 0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85 deleted successfully from bucket 0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b
File deletion request submitted successfully: true
Wait for Backend to Return Empty Bucket¶
Right after your file's deletion is requested, your script will immediately try to delete the bucket your file was in. At this point, the file might not have been deleted, or if it has been, DataHaven’s indexer may not have processed that block yet. Until the indexer catches up, the MSP backend won't show up-to-date data, so any bucket deletion attempt will fail. To avoid that race condition, you’ll add a small polling helper that waits for the indexer to acknowledge your bucket is empty before continuing.
-
Add the following code in your
bucketOperations.tsfile:bucketOperations.tsexport async function waitForBackendBucketEmpty(bucketId: string) { const maxAttempts = 144; // 12 minutes total (144 * 5s) const delayMs = 5000; for (let i = 0; i < maxAttempts; i++) { try { const bucketFiles: FileListResponse = await mspClient.buckets.getFiles(bucketId); if (bucketFiles.files.length === 0) { console.log('Bucket is empty in MSP backend:', bucketFiles); return; } console.log( `Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` + `Attempt ${i + 1}/${maxAttempts}`, ); } catch (error: any) { if (error.status === 404 || error.body.error === 'Not found: Record') { console.log(`Bucket not found in MSP backend (404).`); throw new Error(`Bucket ${bucketId} not found in MSP backend`); } else { console.log('Unexpected error while fetching bucket from MSP:', error); throw error; } } await new Promise((r) => setTimeout(r, delayMs)); } throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`); }
View complete bucketOperations.ts up until this point
import { Bucket, FileListResponse } from '@storagehub-sdk/msp-client';
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
export async function getBucketsFromMSP(): Promise<Bucket[]> {
const buckets: Bucket[] = await mspClient.buckets.listBuckets();
return buckets;
}
export async function waitForBackendBucketEmpty(bucketId: string) {
const maxAttempts = 144; // 12 minutes total (144 * 5s)
const delayMs = 5000;
for (let i = 0; i < maxAttempts; i++) {
try {
const bucketFiles: FileListResponse =
await mspClient.buckets.getFiles(bucketId);
if (bucketFiles.files.length === 0) {
console.log('Bucket is empty in MSP backend:', bucketFiles);
return;
}
console.log(
`Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` +
`Attempt ${i + 1}/${maxAttempts}`,
);
} catch (error: any) {
if (error.status === 404 || error.body.error === 'Not found: Record') {
console.log(`Bucket not found in MSP backend (404).`);
throw new Error(`Bucket ${bucketId} not found in MSP backend`);
} else {
console.log('Unexpected error while fetching bucket from MSP:', error);
throw error;
}
}
await new Promise((r) => setTimeout(r, delayMs));
}
throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`);
}
// Add other helper methods here
-
Update the
index.tsfile to trigger the helper method you just implemented:index.ts // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**// Wait for backend to process deletion and verify bucket is empty await waitForBackendBucketEmpty(bucketId);The response should look something like this:
ts-node index.tsChecking MSP backend for empty bucket... 1 file(s) remaining. Attempt 1/144 Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 2/144 Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 3/144 Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 4/144 Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 5/144 Bucket is empty in MSP backend: { files: [] }/
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// --- Data fetching logic ---
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
// --- Data deleting logic ---
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey,
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful,
);
// Wait for backend to process deletion and verify bucket is empty
await waitForBackendBucketEmpty(bucketId);
// **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Delete a Bucket¶
To delete a bucket, create a helper method called deleteBucket in your bucketOperations.ts file and then update the index.ts file accordingly, to execute that logic.
Note
A bucket can only be deleted if all its files have already been deleted. Use the mspClient.buckets.getFiles() method by passing a bucketId as a parameter to check all the files currently stored in that bucket.
Add Method to Delete Bucket¶
Create a new file within the operations folder called bucketOperations.ts and add the following code:
export async function deleteBucket(bucketId: string): Promise<boolean> {
const txHash: `0x${string}` | undefined = await storageHubClient.deleteBucket(
bucketId as `0x${string}`,
);
console.log('deleteBucket() txHash:', txHash);
if (!txHash) {
throw new Error('deleteBucket() did not return a transaction hash');
}
// Wait for transaction
const receipt = await publicClient.waitForTransactionReceipt({
hash: txHash,
});
console.log('deleteBucket() txReceipt:', receipt);
if (receipt.status !== 'success') {
throw new Error(`Bucket deletion failed: ${txHash}`);
}
return true;
}
Call the Delete Bucket Helper Method¶
Update index.ts with the following code to trigger the deleteBucket helper method you just implemented:
// Delete bucket
const isBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', isBucketDeletionSuccessful);
If you run the script with the bucket deletion code, the response should include:
deleteBucket() txHash: 0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13
deleteBucket() txReceipt: {
transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
transactionIndex: 0,
blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
to: '0x0000000000000000000000000000000000000404',
blockNumber: 117400n,
cumulativeGasUsed: 228432n,
gasUsed: 228432n,
contractAddress: null,
logs: [
{
address: '0x0000000000000000000000000000000000000404',
topics: [Array],
data: '0x',
blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
blockNumber: 117400n,
transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
transactionIndex: 0,
logIndex: 0,
transactionLogIndex: '0x0',
removed: false
}
],
logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000008000008000000000010000000000000000000000000040000000000020000000000000000000000000100000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000010000800000000000000000000000080000',
status: 'success',
effectiveGasPrice: 1000000000n,
type: 'legacy'
}
Bucket deletion successful: true
View complete index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
getBucketFilesFromMSP,
requestDeleteFile,
} from './operations/fileOperations.js';
import {
deleteBucket,
getBucketsFromMSP,
waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';
async function run() {
// Initialize WASM
await initWasm();
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already, convert it with .toHex()
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey,
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful,
);
// Wait for backend to process deletion and verify bucket is empty
await waitForBackendBucketEmpty(bucketId);
// Delete bucket
const isBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', isBucketDeletionSuccessful);
await polkadotApi.disconnect();
}
run();
View complete bucketOperations.ts
import { Bucket, FileListResponse } from '@storagehub-sdk/msp-client';
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
export async function getBucketsFromMSP(): Promise<Bucket[]> {
const buckets: Bucket[] = await mspClient.buckets.listBuckets();
return buckets;
}
export async function waitForBackendBucketEmpty(bucketId: string) {
const maxAttempts = 144; // 12 minutes total (144 * 5s)
const delayMs = 5000;
for (let i = 0; i < maxAttempts; i++) {
try {
const bucketFiles: FileListResponse =
await mspClient.buckets.getFiles(bucketId);
if (bucketFiles.files.length === 0) {
console.log('Bucket is empty in MSP backend:', bucketFiles);
return;
}
console.log(
`Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` +
`Attempt ${i + 1}/${maxAttempts}`,
);
} catch (error: any) {
if (error.status === 404 || error.body.error === 'Not found: Record') {
console.log(`Bucket not found in MSP backend (404).`);
throw new Error(`Bucket ${bucketId} not found in MSP backend`);
} else {
console.log('Unexpected error while fetching bucket from MSP:', error);
throw error;
}
}
await new Promise((r) => setTimeout(r, delayMs));
}
throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`);
}
export async function deleteBucket(bucketId: string): Promise<boolean> {
const txHash: `0x${string}` | undefined = await storageHubClient.deleteBucket(
bucketId as `0x${string}`,
);
console.log('deleteBucket() txHash:', txHash);
if (!txHash) {
throw new Error('deleteBucket() did not return a transaction hash');
}
// Wait for transaction
const receipt = await publicClient.waitForTransactionReceipt({
hash: txHash,
});
console.log('deleteBucket() txReceipt:', receipt);
if (receipt.status !== 'success') {
throw new Error(`Bucket deletion failed: ${txHash}`);
}
return true;
}
View complete fileOperations.ts
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
import { FileInfo } from '@storagehub-sdk/core';
import { FileListResponse } from '@storagehub-sdk/msp-client';
export async function getBucketFilesFromMSP(
bucketId: string,
): Promise<FileListResponse> {
const files: FileListResponse = await mspClient.buckets.getFiles(bucketId);
return files;
}
export async function requestDeleteFile(
bucketId: string,
fileKey: string,
): Promise<boolean> {
// Get file info before deletion
const fileInfo: FileInfo = await mspClient.files.getFileInfo(
bucketId,
fileKey,
);
console.log('File info:', fileInfo);
// Request file deletion
const txHashRequestDeleteFile: `0x${string}` =
await storageHubClient.requestDeleteFile(fileInfo);
console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);
// Wait for delete file transaction receipt
const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
{
hash: txHashRequestDeleteFile,
},
);
console.log('requestDeleteFile() txReceipt:', receiptRequestDeleteFile);
if (receiptRequestDeleteFile.status !== 'success') {
throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
}
console.log(
`File with key ${fileKey} deleted successfully from bucket ${bucketId}`,
);
return true;
}
Next Steps¶
| Created: November 7, 2025