Skip to content

Verify BSP Node via API

This guide walks you through how to register your Backup Storage Provider (BSP) on-chain and verify it is eligible to participate in the DataHaven network through scripts that utilize the Polkadot API.

Prerequisites

  • A running BSP node with your BSP secret/raw seed and your BSP's multiaddress handy.
  • 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:

    1. Create a new project folder by executing the following command in the terminal:

      mkdir datahaven-project && cd datahaven-project
      
    2. Initialize a package.json file using the correct command for your package manager:

      pnpm init
      
      yarn init
      
      npm init --y
      
    3. Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:

      pnpm add -D typescript tsx ts-node @types/node
      
      yarn add -D typescript tsx ts-node @types/node
      
      npm install -D typescript tsx ts-node @types/node
      
    4. Create a tsconfig.json file in the root of your project and paste the following configuration:

      tsconfig.json
      {
          "compilerOptions": {
              "target": "ES2022",
              "module": "nodenext",
              "moduleResolution": "NodeNext",
              "esModuleInterop": true,
              "strict": true,
              "skipLibCheck": true,
              "outDir": "dist",
              "declaration": true,
              "sourceMap": true
          },
          "include": ["src/**/*.ts"]
      }
      
    5. Initialize the src directory:

      mkdir src && touch src/index.ts
      
  • Dependencies installed

  • Clients initialized

Deposit Requirements

The formula for the deposit is as follows:

SpMinDeposit + (capacity_in_gib * DepositPerData) + buffer

  • SpMinDeposit: Base deposit of 100 MOCK
  • capacity_in_gib: The set GiB capacity of your hardware
  • DepositPerData: 2 MOCK per GiB
  • buffer: An additional safety margin

Examples:

  • 800 GiB capacity: 100 + (800 × 2) = 1,700 MOCK required (1,800 MOCK recommended)
  • 1.6 TiB capacity: 100 + (1,638 × 2) = 3,376 MOCK required (3,500+ MOCK recommended)

The deposit is held (reserved) from your account when you start the BSP registration process and remains held while you operate as a BSP. The deposit is returned when you deregister as a BSP.

Note

Your BSP account must be funded before BSP registration.

Install Additional Dependencies

Install the following dependencies:

pnpm add polkadot-api @polkadot/util-crypto

Set Up Keyrings

Set up the keyrings needed to sign BSP-related transactions and derive the correct on-chain identities.

  1. Within your services folder, create a new file called bspService.ts.

  2. Add the following code:

    bspService.ts
    import { ApiPromise, Keyring, WsProvider } from '@polkadot/api';
    import { cryptoWaitReady } from '@polkadot/util-crypto';
    import { types } from '@storagehub/types-bundle';
    
    // Make sure WASM crypto is initialized *before* we create any keypairs
    await cryptoWaitReady();
    
    // Create signer from secret URI
    const walletKeyring = new Keyring({ type: 'ethereum' });
    const signer = walletKeyring.addFromUri('INSERT_PRIVATE_KEY');
    
    // Make sure to use the BSP's seed phrase here
    // generated from following the "Run a BSP Node" steps
    const bspEvmKeyring = new Keyring({ type: 'ethereum' });
    // BSP EVM signer
    const bspEvmSigner = bspEvmKeyring.addFromUri('INSERT_BSP_RAW_SEED');
    
    const bspSubstrateKeyring = new Keyring({ type: 'ecdsa' });
    // BSP Substrate signer
    const bspSubstrateSigner = bspSubstrateKeyring.addFromUri(
      'INSERT_BSP_RAW_SEED'
    );
    // If running BSP node on your local machine
    // create Polkadot API client, but for your actively running BSP node
    const localBSPwsUrl = `ws://127.0.0.1:9946`;
    const provider = new WsProvider(localBSPwsUrl);
    const polkadotApiBsp: ApiPromise = await ApiPromise.create({
      provider,
      typesBundle: types,
      noInitWarn: true,
    });
    
    export { signer, bspEvmSigner, bspSubstrateSigner, polkadotApiBsp };
    

Warning

It is assumed that private keys are securely stored and managed in accordance with standard security practices.

Request BSP Sign Up

Submit an on-chain request to register your node as an official Backup Storage Provider.

Add Helper Methods to Request Sign Up

Add reusable helper methods for funding your BSP account, checking balances, resolving multiaddresses, and submitting the sign-up transaction.

  1. If you haven't already, create an operations folder adjacent to your services folder:

    mkdir operations
    
  2. Create a bspOperations.ts file.

  3. Add the following helper methods to your bspOperations.ts file:

    bspOperations.ts
    import { Binary } from 'polkadot-api';
    import { bspEvmSigner, bspSubstrateSigner } from '../services/bspService.js';
    import {
      chain,
      account,
      publicClient,
      walletClient,
      polkadotApi,
    } from '../services/clientService.js';
    import { formatEther } from 'viem/utils';
    import { polkadotApiBsp } from '../services/bspService.js';
    
    export async function fundBspAddress(
      bspAddress: `0x${string}`,
      amount: bigint
    ) {
      console.log('Recipient:', bspAddress);
      console.log('Amount (raw):', amount.toString());
    
      const txHash = await walletClient.sendTransaction({
        account: account,
        chain: chain,
        to: bspAddress,
        value: amount,
      });
      console.log('Tx hash:', txHash);
    
      const receipt = await publicClient.waitForTransactionReceipt({
        hash: txHash,
      });
      console.log('Finalized in block:', receipt.blockNumber.toString());
    }
    
    export async function checkBspBalance(bspAddress: `0x${string}`) {
      // Query balance
      const balance = formatEther(
        await publicClient.getBalance({ address: bspAddress })
      );
    
      console.log(`BSP EVM Address: ${bspAddress}`);
      console.log(`Balance: ${balance} MOCK`);
    }
    
    export async function getMultiaddresses() {
      try {
        const addresses = await polkadotApiBsp.rpc.system.localListenAddresses();
    
        // addresses are Vec<Text> type. Turn into plain strings
        const stringAddresses = addresses
          .map((a) => a.toString())
          .filter((addr) => !addr.includes('/ip4/127.0.0.1'))
          .filter((addr) => !addr.includes('/ip6/::1'));
        console.log('Local listen addresses:', stringAddresses);
        // Convert multiaddresses to Binary format
        return stringAddresses.map((addr) => Binary.fromText(addr));
      } catch (error) {
        console.error('Error fetching local listen addresses:', error);
        throw new Error('Failed to fetch local listen addresses');
      }
    }
    
    export async function requestBspSignUp(
      bspAddress: string,
      multiaddresses: Binary[],
      capacity: bigint
    ) {
      // Request BSP sign up
      const requestTx = polkadotApi.tx.Providers.request_bsp_sign_up({
        capacity: capacity,
        multiaddresses: multiaddresses,
        payment_account: bspEvmSigner.address, // Account receiving payments
      });
    
      // Sign and submit the request
      await new Promise<void>((resolve, reject) => {
        requestTx
          .signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => {
            console.log('BSP sign-up requested. Waiting for finalization...');
            if (dispatchError) {
              reject(dispatchError);
            }
            if (status.isFinalized) {
              console.log('Request finalized! Deposit has been reserved.');
              resolve();
            }
          })
          .catch(reject);
      });
    }
    
    // Add other BSP helper methods here
    

The roles each helper method plays:

  • fundBspAddress: Ensures the BSP account has enough balance to cover the required deposit.
  • checkBspBalance: Verifies available funds of the BSP address.
  • getMultiaddresses: Fetches the network addresses your BSP advertises to the network and selects the proper multiaddresses for the request_bsp_sign_up extrinsic (needed only if you are running BSP node on your local machine).
  • requestBspSignUp: Submits the on-chain request to register the BSP node by calling the request_bsp_sign_up extrinsic.

Call the Request Sign Up Method

Trigger the BSP sign-up flow from your main script to submit the registration request on-chain.

  1. Create an index.ts file adjacent to your operations and services folder, if you haven't already.

  2. Add the following code:

    index.ts
    import { initWasm } from '@storagehub-sdk/core';
    import { polkadotApi } from './services/clientService';
    import {
      cancelBspSignUp,
      checkBspBalance,
      confirmBspSignUp,
      fundBspAddress,
      getMultiaddresses,
      requestBspSignUp,
    } from './operations/bspOperations';
    import { bspEvmSigner } from './services/bspService';
    
    async function runSignUpRequest() {
      // Initialize WASM
      await initWasm();
    
      // Make sure to set the BSP public key from the seed phrase you saved when following the "Run a BSP Node" steps
      const bspAddress = bspEvmSigner.address as `0x${string}`;
      // console.log('bspSigner.address:', bspSigner.address);
    
      // Amount in smallest units of the native token.
      const depositAmount = 200_000_000_000_000_000_000n;
    
      await fundBspAddress(bspAddress, depositAmount);
    
      await checkBspBalance(bspAddress);
    
      // BSP configuration
    
      const multiaddresses = ['INSERT_MULTIADDRESS'];
      // If running BSP on your local machine
      // get multiaddresses from the BSP node directly by uncommenting the line below
      // const multiaddresses = await getMultiaddresses();
    
      // Capacity should match the capacity set in docker-compose.yml
      // This is just an example value.
      const capacity = BigInt(10_737_418_240); // 10 GiB (80% of 12 GiB disk)
    
      await requestBspSignUp(bspAddress, multiaddresses, capacity);
    
      await polkadotApi.disconnect();
    }
    
    // Add other script methods here
    

    Check the formula for the deposit to make sure you've prepared the right amount of funds.

Cancel BSP Sign Up If Needed

Mistakes can happen during the BSP verification process. That's why the cancel_sign_up extrinsic is available after the request_bsp_sign_up has been called in order to revoke the request before it has been finalized on-chain. This extrinsic can only be called prior to confirming verification via the confirm_sign_up extrinsic.

Add Helper Method to Cancel Sign Up

Add the cancelBspSignUp helper method that submits the transaction to cancel a pending BSP registration.

Add the following code to your bspOperations.ts file:

bspOperations.ts
export async function cancelBspSignUp() {
  // Cancel BSP sign-up
  const cancelTx = polkadotApi.tx.Providers.cancel_sign_up({});

  await new Promise<void>((resolve, reject) => {
    cancelTx
      .signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => {
        console.log('Cancelling BSP registration...');
        if (dispatchError) {
          reject(dispatchError);
        }
        if (status.isFinalized) {
          console.log('BSP registration cancelled.');
          resolve();
        }
      })
      .catch(reject);
  });
}

Call the Cancel Sign Up Method

Invoke the cancel method from your script to abort the BSP sign-up process, with the following code:

index.ts
async function runSignUpCancel() {
  // Initialize WASM
  await initWasm();

  // Registration can only be cancelled before the confirm_sign_up extrinsic is called
  await cancelBspSignUp();

  await polkadotApi.disconnect();
}
// Uncomment to run the cancel function
// runSignUpCancel();

Confirm BSP Sign Up

Confirm your BSP registration after the required waiting period has passed. You should only trigger the confirmBspSignUp method after a new epoch has begun. An epoch lasts 1 hour. You can check if a new epoch has begun on the Explorer page on Polkadot.js Apps.

Add Helper Method to Confirm Sign Up

To add the confirmBspSignUp helper method and thus finalize the BSP registration on-chain, add the following code to your bspOperations.ts file:

bspOperations.ts
export async function confirmBspSignUp() {
  // Confirm the sign-up (after waiting for randomness)
  const confirmTx = polkadotApi.tx.Providers.confirm_sign_up({
    provider_account: undefined, // Optional: omit to use signer's account
  });

  await new Promise<void>((resolve, reject) => {
    confirmTx
      .signAndSend(bspSubstrateSigner, ({ status, dispatchError }) => {
        console.log('Confirming BSP registration...');
        if (dispatchError) {
          reject(dispatchError);
        }
        if (status.isFinalized) {
          console.log('BSP registration confirmed and active!');
          resolve();
        }
      })
      .catch(reject);
  });
}

Call the Confirm Sign Up Method

Call the confirmation method with the following code to complete BSP registration:

index.ts
async function runSignUpConfirm() {
  // Initialize WASM
  await initWasm();

  await confirmBspSignUp();

  await polkadotApi.disconnect();
}
// Make sure to run `runSignUpRequest()` first and to
// wait accordingly before running `runSignUpConfirm()`
// as explained in the documentation

runSignUpRequest();
// runSignUpConfirm();

Next Steps

Last update: January 14, 2026
| Created: January 14, 2026