
Product
Socket Now Protects the Firefox Extension Ecosystem
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.
@dedot/api
Advanced tools
A delightful JavaScript/TypeScript client for Polkadot & Substrate
Note: The project is still in active development phase, the information on this page might be outdated. Feel free to raise an issue if you run into any problems or want to share any ideas.
@polkadot/api, easy & fast migration!@polkadot/extension-based walletsTry dedot now on CodeSandbox Playground or follow the below steps to run it on your local environment.
dedot package# via yarn
yarn add dedot
# via npm
npm i dedot
@dedot/chaintypes package for chain types & APIs suggestion. Skip this step if you don't use TypeScript.# via yarn
yarn add -D @dedot/chaintypes
# via npm
npm i -D @dedot/chaintypes
// main.ts
import { Dedot } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
const run = async () => {
const api = await Dedot.new<PolkadotApi>('wss://rpc.polkadot.io');
// Call rpc `state_getMetadata` to fetch raw scale-encoded metadata and decode it.
const metadata = await api.rpc.state.getMetadata();
console.log('Metadata:', metadata);
// Query on-chain storage
const balance = await api.query.system.account(<address>);
console.log('Balance:', balance);
// Subscribe to on-chain storage changes
const unsub = await api.query.system.number((blockNumber) => {
console.log(`Current block number: ${blockNumber}`);
});
// Get pallet constants
const ss58Prefix = api.consts.system.ss58Prefix;
console.log('Polkadot ss58Prefix:', ss58Prefix);
// Call runtime api
const pendingRewards = await api.call.nominationPoolsApi.pendingRewards(<address>)
console.log('Pending rewards:', pendingRewards);
// await unsub();
// await api.disconnect();
}
run().catch(console.error);
dedot using require.// main.js
const { Dedot } = require('dedot');
// ...
const api = await Dedot.new('wss://rpc.polkadot.io');
@polkadot/api -> dedot| Feature | Status |
|---|---|
Execute RPC (api.rpc) | ✅ |
Query On-chain Storage (api.query) | ✅ |
Get runtime constants (api.consts) | ✅ |
Runtime APIs (api.call) | ✅ |
Transaction APIs (api.tx) | ✅ |
Events (api.events) | ✅ |
Errors (api.errors) | ✅ |
| Contract APIs | ⏳ |
| Metadata v14 | ✅ |
| Metadata v15 | ✅ |
| RPC v2 | ⏳ |
| Extrinsic V5 | ⏳ |
Each Substrate-based blockchain has their own set of data types & APIs to interact with, so being aware of those types & APIs when working with a blockchain will greatly improve the overall development experience. dedot exposes TypeScript's types & APIs for each individual Substrate-based blockchain, we recommend using TypeScript for your project to have the best experience.
Types & APIs for each Substrate-based blockchains are defined in package @dedot/chaintypes:
# via yarn
yarn add -D @dedot/chaintypes
# via npm
npm i -D @dedot/chaintypes
Initialize a Dedot instance using the ChainApi interface for a target chain to enable types & APIs suggestion/autocompletion for that particular chain:
import { Dedot } from 'dedot';
import type { PolkadotApi, KusamaApi, MoonbeamApi, AstarApi } from '@dedot/chaintypes';
// ...
const polkadotApi = await Dedot.new<PolkadotApi>('wss://rpc.polkadot.io');
console.log(await polkadotApi.query.babe.authorities());
const kusamaApi = await Dedot.new<KusamaApi>('wss://kusama-rpc.polkadot.io');
console.log(await kusamaApi.query.society.memberCount());
const moonbeamApi = await Dedot.new<MoonbeamApi>('wss://wss.api.moonbeam.network');
console.log(await moonbeamApi.query.ethereumChainId.chainId());
const astarApi = await Dedot.new<AstarApi>('wss://rpc.astar.network');
console.log(await astarApi.query.dappsStaking.blockRewardAccumulator());
const genericApi = await Dedot.new('ws://localhost:9944');
// ...
Supported ChainApi interfaces are defined here, you can also generate the ChainApi interface for the chain you want to connect with using @dedot/cli.
# Install @dedot/cli via yarn
yarn add -D @dedot/cli
# Or via npm
npm i -D @dedot/cli
# Generate ChainApi interface for Polkadot network via rpc endpoint: wss://rpc.polkadot.io
npx dedot chaintypes -w wss://rpc.polkadot.io
RPCs can be executed via api.rpc entry point. After creating a Dedot instance with a ChainApi interface of the network you want to interact with, all RPC methods of the network will be exposed in the autocompletion/suggestion with format: api.rpc.<module>.<methodName>. E.g: you can find all supported RPC methods for Polkadot network here, similarly for other networks as well.
Examples:
// Call rpc: `state_getMetadata`
const metadata = await api.rpc.state.getMetadata();
// Call an arbitrary rpc: `module_rpc_name` with arguments ['param1', 'param2']
const result = await api.rpc.module.rpc_name('param1', 'param2');
On-chain storage can be queried via api.query entry point. All the available storage entries for a chain are exposed in the ChainApi interface for that chain and can be executed with format: api.query.<pallet>.<storgeEntry>. E.g: You can find all the available storage queries of Polkadot network here, similarly for other networks as well.
Examples:
// Query account balance
const balance = await api.query.system.account(<address>);
// Get all events of current block
const events = await api.query.system.events();
Runtime constants (parameter types) are defined in metadata, and can be inspected via api.consts entry point with format: api.consts.<pallet>.<constantName>. All available constants are also exposed in the ChainApi interface. E.g: Available constants for Polkadot network is defined here, similarly for other networks.
Examples:
// Get runtime version
const runtimeVersion = api.consts.system.version;
// Get existential deposit in pallet balances
const existentialDeposit = api.consts.balances.existentialDeposit;
The latest stable Metadata V15 now includes all the runtime apis type information. So for chains that are supported Metadata V15, we can now execute all available runtime apis with syntax api.call.<runtimeApi>.<methodName>, those apis are exposed in ChainApi interface. E.g: Runtime Apis for Polkadot network is defined here, similarly for other networks as well.
Examples:
// Get account nonce
const nonce = await api.call.accountNonceApi.accountNonce(<address>);
// Query transaction payment info
const tx = api.tx.balances.transferKeepAlive(<address>, 2_000_000_000_000n);
const queryInfo = await api.call.transactionPaymentApi.queryInfo(tx.toU8a(), tx.length);
// Get runtime version
const runtimeVersion = await api.call.core.version();
For chains that only support Metadata V14, we need to bring in the Runtime Api definitions when initializing the Dedot client instance to encode & decode the calls. You can find all supported Runtime Api definitions in @dedot/runtime-specs package.
Examples:
import { RuntimeApis } from '@dedot/runtime-specs';
const api = await Dedot.new({ endpoint: 'wss://rpc.mynetwork.com', runtimeApis: RuntimeApis });
// Or bring in only the Runtime Api definition that you want to interact with
import { AccountNonceApi } from '@dedot/runtime-specs';
const api = await Dedot.new({ endpoint: 'wss://rpc.mynetwork.com', runtimeApis: { AccountNonceApi } });
// Get account nonce
const nonce = await api.call.accountNonceApi.accountNonce(<address>);
You absolutely can define your own Runtime Api definition if you don't find it in the supported list.
Transaction apis are designed to be compatible with IKeyringPair and Signer interfaces, so you can sign the transactions with accounts created by a Keyring or from any Polkadot{.js}-based wallet extensions.
All transaction apis are exposed in ChainApi interface and can be access with syntax: api.tx.<pallet>.<transactionName>. E.g: Available transaction apis for Polkadot network are defined here, similarly for other networks as well.
Example 1: Sign transaction with a Keying account
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { Keyring } from '@polkadot/keyring';
...
await cryptoWaitReady();
const keyring = new Keyring({ type: 'sr25519' });
const alice = keyring.addFromUri('//Alice');
const unsub = await api.tx.balances
.transferKeepAlive(<destAddress>, 2_000_000_000_000n)
.signAndSend(alice, async ({ status }) => {
console.log('Transaction status', status.tag);
if (status.tag === 'InBlock') {
console.log(`Transaction completed at block hash ${status.value}`);
await unsub();
}
});
Example 2: Sign transaction using Signer from Polkadot{.js} wallet extension
const injected = await window.injectedWeb3['polkadot-js'].enable('A cool dapp');
const account = (await injected.accounts.get())[0];
const signer = injected.signer;
const unsub = await api.tx.balances
.transferKeepAlive(<destAddress>, 2_000_000_000_000n)
.signAndSend(account.address, { signer }, async ({ status }) => {
console.log('Transaction status', status.tag);
if (status.tag === 'InBlock') {
console.log(`Transaction completed at block hash ${status.value}`);
await unsub();
}
});
Example 3: Submit a batch transaction
import type { PolkadotRuntimeRuntimeCallLike } from '@dedot/chaintypes/polkadot';
// Omit the detail for simplicity
const account = ...;
const signer = ...;
const transferTx = api.tx.balances.transferKeepAlive(<destAddress>, 2_000_000_000_000n);
const remarkCall: PolkadotRuntimeRuntimeCallLike = {
pallet: 'System',
palletCall: {
name: 'RemarkWithEvent',
params: {
remark: 'Hello Dedot!',
},
},
};
const unsub = api.tx.utility.batch([transferTx.call, remarkCall])
.signAndSend(account.address, { signer }, async ({ status }) => {
console.log('Transaction status', status.tag);
if (status.tag === 'InBlock') {
console.log(`Transaction completed at block hash ${status.value}`);
await unsub();
}
});
import { WestendAssetHubApi, XcmVersionedLocation, XcmVersionedAssets, XcmV3WeightLimit } from '@dedot/chaintypes/westendAssetHub';
import { AccountId32 } from '@dedot/codecs';
const TWO_TOKENS = 2_000_000_000_000n;
const destAddress = <bobAddress>;
const api = await Dedot.new<WestendAssetHubApi>('...westend-assethub-rpc...');
const dest: XcmVersionedLocation = {
tag: 'V3',
value: { parents: 1, interior: { tag: 'Here' } },
};
const beneficiary: XcmVersionedLocation = {
tag: 'V3',
value: {
parents: 0,
interior: {
tag: 'X1',
value: {
tag: 'AccountId32',
value: { id: new AccountId32(destAddress).raw },
},
},
},
};
const assets: XcmVersionedAssets = {
tag: 'V3',
value: [
{
id: {
tag: 'Concrete',
value: {
parents: 1,
interior: { tag: 'Here' },
},
},
fun: {
tag: 'Fungible',
value: TWO_TOKENS,
},
},
],
};
const weight: XcmV3WeightLimit = { tag: 'Unlimited' };
api.tx.polkadotXcm
.limitedTeleportAssets(dest, beneficiary, assets, 0, weight)
.signAndSend(alice, { signer, tip: 1_000_000n }, (result) => {
console.dir(result, { depth: null });
});
Events for each pallet emit during runtime operations and are defined in the medata. Available events are also exposed in ChainApi interface so we can get information of an event through syntax api.events.<pallet>.<eventName>. E.g: Events for Polkadot network can be found here, similarly for other network as well.
This api.events is helpful when we want quickly check if an event matches with an event that we're expecting in a list of events, the API also comes with type narrowing for the matched event, so event name & related data of the event are fully typed.
Example to list new accounts created in each block:
// ...
const ss58Prefix = api.consts.system.ss58Prefix;
await api.query.system.events(async (eventRecords) => {
const newAccountEvents = eventRecords
.map(({ event }) => api.events.system.NewAccount.as(event))
.filter((one) => one);
console.log(newAccountEvents.length, 'account(s) was created in block', await api.query.system.number());
newAccountEvents.forEach((event, index) => {
console.log(`New Account ${index + 1}:`, event.palletEvent.data.account.address(ss58Prefix));
});
});
// ...
Pallet errors are thrown out when things go wrong in the runtime, those are defined in the metadata. Available errors for each pallet are also exposed in ChainApi interface, so we can get information an error through this syntax: api.errors.<pallet>.<errorName>. E.g: Available errors for Polkadot network can be found here.
Similar to events API, this API is helpful when we want to check if an error maches with an error that we're expecting.
Example if an error is AlreadyExists from Assets pallet:
// ...
await api.query.system.events(async (eventRecords) => {
for (const tx of eventRecords) {
if (api.events.system.ExtrinsicFailed.is(tx.event)) {
const { dispatchError } = tx.event.palletEvent.data;
if (dispatchError.tag === 'Module' && api.errors.assets.AlreadyExists.is(dispatchError.value)) {
console.log('Assets.AlreadyExists error occurred!');
} else {
console.log('Other error occurred', dispatchError);
}
}
}
});
// ...
@polkadot/api to dedotdedot is inspired by @polkadot/api, so both are sharing some common patterns and api styling (eg: api syntax api.<type>.<module>.<section>). Although we have experimented some other different api stylings but to our findings and development experience, we find that the api style of @polkadot/api is very intuiative and easy to use. We decide the use a similar api styling with @polkadot/api, this also helps the migration from @polkadot/api to dedot easier & faster.
While the api style are similar, but there're also some differences you might need to be aware of when switching to use dedot.
Initialize api client
@polkadot/apiimport { ApiPromise, WsProvider } from '@polkadot/api';
const api = await ApiPromise.create({ provider: new WsProvider('wss://rpc.polkadot.io') });
dedotimport { Dedot } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
const api = await Dedot.new<PolkadotApi>('wss://rpc.polkadot.io'); // or Dedot.create(...) if you prefer
// OR you want to have a custom provider
import { WsProvider } from 'dedot'
const api = await Dedot.new<PolkadotApi>({ provider: new WsProvider('wss://rpc.polkadot.io') });
WsProvider will be created if you initialize Dedot directly with an rpc endpoint.dedot only supports provider can make subscription request (e.g: via Websocket).ChainApi interface (e.g: PolkadotApi in the example above) of the chain that you want to interact with. This enable apis & types suggestion/autocompletion for that particular chain (via IntelliSense). If you don't specify a ChainApi interface, the default SubstrateApi interface will be used.Type system
Unlike @polkadot/api where data are wrapped inside a codec types, so we always need to unwrap the data before using it (e.g: via .unwrap(), .toNumber(), .toString(), .toJSON() ...). dedot leverages the native TypeScript type system to represent scale-codec types, so you can use the data directly without extra handling/unwrapping. The table below is a mapping between scale-codec types and TypeScript types that we're using for dedot:
| Scale Codec | TypeScript (dedot) |
|---|---|
u8, u16, u32, i8, i16, i32 | number |
u64, u128, u256, i64, i128, i256 | bigint (native BigInt, not bn.js) |
bool | boolean (true, false) |
Option<T> | T | undefined |
Result<Ok, Err> | { isOk: true; isErr?: false; value: Ok } | { isOk?: false; isErr: true; err: Err } |
Vec<T> | Array<T> |
str | string |
Tuple: (A, B), () | [A, B], [] |
Struct: struct { field_1: u8, field_2: str } | { field_1: number, field_2: string} |
Enum: enum { Variant1(u8), Variant2(bool), Variant3 } | { tag: 'Variant1', value: number } | { tag: 'Variant2', value: boolean } | { tag: 'Variant2' } |
FlatEnum: enum { Variant1, Variant2 } | 'Variant1' | 'Variant2' |
E.g 1:
const runtimeVersion = api.consts.system.version;
// @polkadot/api
const specName: string = runtimeVersion.toJSON().specName; // OR runtimeVersion.specName.toString()
// dedot
const specName: string = runtimeVersion.specName;
E.g 2:
const balance = await api.query.system.account(<address>);
// @polkadot/api
const freeBalance: bigint = balance.data.free.toBigInt();
// dedot
const freeBalance: bigint = balance.data.free;
E.g 3:
// @polkadot/api
const proposalBondMaximum: bigint | undefined = api.consts.treasury.proposalBondMaximum.unwrapOr(undefined)?.toBigInt();
// dedot
const proposalBondMaximum: bigint | undefined = api.consts.treasury.proposalBondMaximum;
dedot take a lot of inspirations from project @polkadot/api. A big thank to all the maintainers/contributors of this awesome library.
Proudly supported by Web3 Foundation Grants Program.
FAQs
A delightful JavaScript/TypeScript client for Polkadot & Substrate
The npm package @dedot/api receives a total of 544 weekly downloads. As such, @dedot/api popularity was classified as not popular.
We found that @dedot/api demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.

Research
/Security News
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.