Skip to content

Dedot

Introduction

Dedot is a next-generation JavaScript client for Polkadot and Polkadot SDK-based blockchains. Designed to elevate the dApp development experience, Dedot is built and optimized to be lightweight and tree-shakable, offering precise types and APIs suggestions for individual Polkadot SDK-based blockchains and ink! smart contracts.

Key Features

  • Lightweight and tree-shakable – no more bn.js or WebAssembly blobs, optimized for dapps bundle size

  • Fully typed API – comprehensive TypeScript support for seamless on-chain interaction and ink! smart contract integration

  • Multi-version JSON-RPC support – compatible with both legacy and new JSON-RPC APIs for broad ecosystem interoperability

  • Light client support – designed to work with light clients such as Smoldot

  • Native TypeScript for scale codec – implements scale codec parsing directly in TypeScript without relying on custom wrappers

  • Wallet integration – works out-of-the-box with @polkadot/extension-based wallets

  • Familiar API design – similar API style to Polkadot.js for easy and fast migration

Installation

To add Dedot to your project, use the following command:

npm i dedot
pnpm add dedot
yarn add dedot

To enable auto-completion/IntelliSense for individual chains, install the @dedot/chaintypes package as a development dependency:

npm i -D @dedot/chaintypes
pnpm add -D @dedot/chaintypes
yarn add -D @dedot/chaintypes

Get Started

Initialize a Client Instance

To connect to and interact with different networks, Dedot provides two client options depending on your needs:

Use the following snippets to connect to Polkadot using DedotClient:

import { DedotClient, WsProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';

// Initialize providers & clients
const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await DedotClient.new<PolkadotApi>(provider);
import { DedotClient, SmoldotProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
import * as smoldot from 'smoldot';

// import `polkadot` chain spec to connect to Polkadot
import { polkadot } from '@substrate/connect-known-chains';

// Start smoldot instance & initialize a chain
const client = smoldot.start();
const chain = await client.addChain({ chainSpec: polkadot });

// Initialize providers & clients
const provider = new SmoldotProvider(chain);
const client = await DedotClient.new<PolkadotApi>(provider);

If the node doesn't support new JSON-RPC APIs yet, you can connect to the network using the LegacyClient, which is built on top of the legacy JSON-RPC APIs.

import { LegacyClient, WsProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';

const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await LegacyClient.new<PolkadotApi>(provider);

Enable Type and API Suggestions

It is recommended to specify the ChainApi interface (e.g., PolkadotApi in the example in the previous section) of the chain you want to interact with. This enables type and API suggestions/autocompletion for that particular chain (via IntelliSense). If you don't specify a ChainApi interface, a default SubstrateApi interface will be used.

import { DedotClient, WsProvider } from 'dedot';
import type { PolkadotApi, KusamaApi } from '@dedot/chaintypes';

const polkadotClient = await DedotClient.new<PolkadotApi>(
  new WsProvider('wss://rpc.polkadot.io')
);
const kusamaClient = await DedotClient.new<KusamaApi>(
  new WsProvider('wss://kusama-rpc.polkadot.io')
);
const genericClient = await DedotClient.new(
  new WsProvider('ws://localhost:9944')
);

If you don't find the ChainApi for the network you're working with in the list, you can generate the ChainApi (types and APIs) using the built-in dedot cli.

# Generate ChainApi interface for Polkadot network via rpc endpoint: wss://rpc.polkadot.io
npx dedot chaintypes -w wss://rpc.polkadot.io

Or open a pull request to add your favorite network to the @dedot/chaintypes repo.

Read On-Chain Data

Dedot provides several ways to read data from the chain:

  • Access runtime constants - use the syntax client.consts.<pallet>.<constantName> to inspect runtime constants (parameter types):

    const ss58Prefix = client.consts.system.ss58Prefix;
    console.log('Polkadot ss58Prefix:', ss58Prefix);
    
  • Storage queries - use the syntax client.query.<pallet>.<storgeEntry> to query on-chain storage:

    const balance = await client.query.system.account('INSERT_ADDRESS');
    console.log('Balance:', balance.data.free);
    
  • Subscribe to storage changes:

    const unsub = await client.query.system.number((blockNumber) => {
      console.log(`Current block number: ${blockNumber}`);
    });
    
  • Call Runtime APIs - use the syntax client.call.<runtimeApi>.<methodName> to execute Runtime APIs:

    const metadata = await client.call.metadata.metadataAtVersion(15);
    console.log('Metadata V15', metadata);
    
  • Watch on-chain events - use the syntax client.events.<pallet>.<eventName> to access pallet events:

    const unsub = await client.events.system.NewAccount.watch((events) => {
      console.log('New Account Created', events);
    });
    

Sign and Send Transactions

Sign the transaction using IKeyringPair from Keyring (@polkadot/keyring) and send the transaction.

import { cryptoWaitReady } from '@polkadot/util-crypto';
import { Keyring } from '@polkadot/keyring';
// Setup keyring
await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
const alice = keyring.addFromUri('//Alice');
// Send transaction
const unsub = await client.tx.balances
  .transferKeepAlive('INSERT_DEST_ADDRESS', 2_000_000_000_000n)
  .signAndSend(alice, async ({ status }) => {
    console.log('Transaction status', status.type);
    if (status.type === 'BestChainBlockIncluded') {
      console.log(`Transaction is included in best block`);
    }
    if (status.type === 'Finalized') {
      console.log(
        `Transaction completed at block hash ${status.value.blockHash}`
      );
      await unsub();
    }
  });

You can also use Signer from wallet extensions:

const injected = await window.injectedWeb3['polkadot-js'].enable('My dApp');
const account = (await injected.accounts.get())[0];
const signer = injected.signer;
const unsub = await client.tx.balances
  .transferKeepAlive('INSERT_DEST_ADDRESS', 2_000_000_000_000n)
  .signAndSend(account.address, { signer }, async ({ status }) => {
    console.log('Transaction status', status.type);
    if (status.type === 'BestChainBlockIncluded') {
      console.log(`Transaction is included in best block`);
    }
    if (status.type === 'Finalized') {
      console.log(
        `Transaction completed at block hash ${status.value.blockHash}`
      );
      await unsub();
    }
  });

Where to Go Next

For more detailed information about Dedot, check the official documentation.

Last update: May 12, 2025
| Created: May 12, 2025