Manage Files and Buckets¶
This guide explains how to manage your storage resources on DataHaven using the StorageHub SDK. You will learn how to request the removal of a file from the network and how to 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
Install Dependencies¶
Initialize Clients and Authenticate MSP Client¶
First, you'll need to set up the necessary clients to connect to the DataHaven network, which runs on a dual-protocol architecture (Substrate for core logic and EVM for compatibility).
If you've already followed the Upload a File guide, your clients may already be initialized and your MSP client authenticated. In that case, review the placeholders at the bottom of the following snippet to see where you'll add logic in this guide, then skip ahead to Request file deletion.
Create an index.ts and add the following code:
import '@storagehub/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from '@storagehub/types-bundle';
import {
HttpClientConfig,
StorageHubClient,
initWasm,
} from '@storagehub-sdk/core';
import { HealthStatus, MspClient, FileInfo } from '@storagehub-sdk/msp-client';
import {
Chain,
PublicClient,
WalletClient,
createPublicClient,
createWalletClient,
defineChain,
http,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
async function run() {
// For anything from @storagehub-sdk/core to work, initWasm() is required
// on top of the file
await initWasm();
// --- viem setup ---
// Define DataHaven chain, as expected by viem
const chain: Chain = defineChain({
id: 55931,
name: 'DataHaven Testnet',
nativeCurrency: { name: 'Mock', symbol: 'MOCK', decimals: 18 },
rpcUrls: {
default: { http: ['https://services.datahaven-testnet.network/testnet'] },
},
});
// Define account from a private key
const account = privateKeyToAccount('INSERT_PRIVATE_KEY' as `0x${string}`);
const address = account.address;
// Create a wallet client using the defined chain, account, and RPC URL
const walletClient: WalletClient = createWalletClient({
chain,
account,
transport: http('https://services.datahaven-testnet.network/testnet'),
});
// Create a public client using the defined chain and RPC URL
const publicClient: PublicClient = createPublicClient({
chain,
transport: http('https://services.datahaven-testnet.network/testnet'),
});
// --- Polkadot.js API setup ---
const provider = new WsProvider(
'wss://services.datahaven-testnet.network/testnet'
);
const polkadotApi: ApiPromise = await ApiPromise.create({
provider,
typesBundle: types,
noInitWarn: true,
});
// Initialize StorageHub Client
const storageHubClient = new StorageHubClient({
rpcUrl: 'https://services.datahaven-testnet.network/testnet',
chain: chain,
walletClient: walletClient,
filesystemContractAddress:
'0x0000000000000000000000000000000000000404' as `0x${string}`,
});
// --- Data deletion logic ---
// **PLACEHOLDER FOR STEP 1: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 2: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Request File Deletion¶
Add the code below to remove a specific file from the DataHaven network. You’ll first fetch the file’s metadata from the MSP, format it for on-chain compatibility, and then submit a deletion request using the StorageHub SDK.
const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
// If not in hex already convert it with .toHex()
// Get file info before deletion
const fileInfo: FileInfo = await mspClient.files.getFileInfo(
bucketId,
fileKey
);
console.log('File info before deletion:', fileInfo);
let formattedFileInfo: any = fileInfo;
['fileKey', 'fingerprint', 'bucketId'].forEach(
(k) => (formattedFileInfo[k] = '0x' + formattedFileInfo[k])
);
console.log('Formatted file info for deletion:', formattedFileInfo);
// Request file deletion
const txHashRequestDeleteFile: `0x${string}` =
await storageHubClient.requestDeleteFile(formattedFileInfo);
console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);
// Wait for delete file transaction receipt
const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
{
hash: txHashRequestDeleteFile,
}
);
console.log('File deletion receipt:', receiptRequestDeleteFile);
if (receiptRequestDeleteFile.status !== 'success') {
throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
}
console.log(
`File with key ${fileKey} deleted successfully from bucket ${bucketId}`
);
If you run the script with the code above, the fileInfo and formattedFileInfo should look like this:
File info before deletion: {
fileKey: 'e0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933',
fingerprint: '1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
bucketId: '5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108',
location: 'helloworld.txt',
size: 18,
isPublic: true,
uploadedAt: 2025-11-05T14:19:55.911Z,
status: 'expired'
}
Formatted file info before deletion:{
fileKey: '0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933',
fingerprint: '0x1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
bucketId: '0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108',
location: 'helloworld.txt',
size: 18,
isPublic: true,
uploadedAt: 2025-11-05T14:19:55.911Z,
status: 'expired'
}
And the final response should include:
requestDeleteFile() txHash: 0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c
File deletion receipt: {
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 0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933 deleted successfully from bucket 0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108
Delete a Bucket¶
To delete your bucket, add the following code:
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.
// Delete bucket
const txHashDeleteBucket: `0x${string}` | undefined =
await storageHubClient.deleteBucket(bucketId as `0x${string}`);
console.log('deleteBucket() txHash:', txHashDeleteBucket);
if (!txHashDeleteBucket) {
throw new Error('deleteBucket() did not return a transaction hash');
}
// Wait for transaction
const receiptDeleteBucket = await publicClient.waitForTransactionReceipt({
hash: txHashDeleteBucket,
});
console.log('Bucket deletion receipt:', receiptDeleteBucket);
if (receiptDeleteBucket.status !== 'success') {
throw new Error(`Bucket deletion failed: ${txHashDeleteBucket}`);
}
If you run the script with the bucket deletion code, the response should include:
deleteBucket() txHash: 0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13
Bucket deletion receipt: {
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'
}
Next Steps¶
| Created: November 7, 2025