Delightful JavaScript/TypeScript client for Polkadot & Substrate
- ✅ Small bundle size, tree-shakable (no more bn.js or wasm-blob tight dependencies)
- ✅ Types & APIs suggestions for each individual Substrate-based blockchain network (@dedot/chaintypes)
- ✅ Familiar api style with
@polkadot/api
, easy & fast migration! - ✅ Native TypeScript type system for scale-codec
- ✅ Compatible with
@polkadot/extension
-based wallets - ✅ Support Metadata V14, V15 (latest)
- ✅ Built-in metadata caching mechanism
- ✅ Build on top of both the new & legacy ( deprecated soon) JSON-RPC APIs
- ✅ Support light clients (e.g: smoldot) (docs coming soon)
- ✅ Typed Contract APIs
- ✅ Fully-typed low-level JSON-RPC client
- ⏳ Compact Metadata
- Getting started
- Example Dapps & Scripts
- Chain Types & APIs
- Execute JSON-RPC Methods
- Query On-chain Storage
- Constants
- Runtime APIs
- Submit Transactions
- Events
- Errors
- Interact with ink! Smart Contracts
@polkadot/api
->dedot
- Packages Structure
- Credit
- Try Dedot! - https://try.dedot.dev - Source Code
- Tiny Url - https://link.dedot.dev - Source Code
- Simple Playground Script
- Interact with PSP22 ink! Contract
- Add yours?
Follow the below steps to install Dedot to your project.
- Install
dedot
package
# via yarn
yarn add dedot
# via npm
npm i dedot
- Install
@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
- Initialize
DedotClient
and start interacting with Polkadot network
// main.ts
import { DedotClient, WsProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
const run = async () => {
const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await DedotClient.new<PolkadotApi>(provider);
// Call rpc `state_getMetadata` to fetch raw scale-encoded metadata and decode it.
const metadata = await client.rpc.state_getMetadata();
console.log('Metadata:', metadata);
// Query on-chain storage
const balance = await client.query.system.account(<address>);
console.log('Balance:', balance);
// Subscribe to on-chain storage changes
const unsub = await client.query.system.number((blockNumber) => {
console.log(`Current block number: ${blockNumber}`);
});
// Get pallet constants
const ss58Prefix = client.consts.system.ss58Prefix;
console.log('Polkadot ss58Prefix:', ss58Prefix);
// Call runtime api
const pendingRewards = await client.call.nominationPoolsApi.pendingRewards(<address>)
console.log('Pending rewards:', pendingRewards);
// await unsub();
// await client.disconnect();
}
run().catch(console.error);
You can also import dedot
using require
.
// main.js
const { DedotClient, WsProvider } = require('dedot');
// ...
const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await DedotClient.new(provider);
If the JSON-RPC server doesn't support new JSON-RPC APIs yet, you can connect to the network using the LegacyClient
which build on top of the legacy JSON-RPC APIs.
import { LegacyClient, WsProvider } from 'dedot';
const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await LegacyClient.new(provider);
[!NOTE] The new JSON-RPC APIs are not well implemented/unstable for RPC Nodes using Polkadot-SDK version <
1.11.0
, so one should connect to the network usingLegacyClient
in such cases. For nodes using Polkadot-SDK version >=1.11.0
, it's recommended to useDedotClient
to connect to the network.You can easily check the current node's implementation version by calling RPC
system_version
:const version = await client.rpc.system_version();
[!NOTE] It's recommended to use
DedotClient
for better performance when you connect to the network using smoldot light client viaSmoldotProvider
.
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 DedotClient
instance using the ChainApi
interface for a target chain to enable types & APIs suggestion/autocompletion for that particular chain:
import { DedotClient, WsProvider } from 'dedot';
import type { PolkadotApi, KusamaApi, MoonbeamApi, AstarApi } from '@dedot/chaintypes';
// ...
const polkadotClient = await DedotClient.new<PolkadotApi>(new WsProvider('wss://rpc.polkadot.io'));
console.log(await polkadotClient.query.babe.authorities());
const kusamaClient = await DedotClient.new<KusamaApi>(new WsProvider('wss://kusama-rpc.polkadot.io'));
console.log(await kusamaClient.query.society.memberCount());
const moonbeamClient = await DedotClient.new<MoonbeamApi>(new WsProvider('wss://wss.api.moonbeam.network'));
console.log(await moonbeamClient.query.ethereumChainId.chainId());
const astarClient = await DedotClient.new<AstarApi>(new WsProvider('wss://rpc.astar.network'));
console.log(await astarClient.query.dappsStaking.blockRewardAccumulator());
const client = await DedotClient.new(new WsProvider('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.
# 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 client.rpc
entry point. After creating a DedotClient
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: client.rpc.method_name(param1, param2, ...)
. 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 client.rpc.state_getMetadata();
// Call an arbitrary rpc: `module_rpc_name` with arguments ['param1', 'param2']
const result = await client.rpc.module_rpc_name('param1', 'param2');
For advanced users who want to interact directly with server/node via raw JSON-RPC APIs, you can use a light-weight JsonRpcClient
for this purpose without having to use DedotClient
or LegacyClient
.
import { JsonRpcClient, WsProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
const provider = new WsProvider('wss://rpc.polkadot.io');
const client = await JsonRpcClient.new<PolkadotApi>(provider);
const chain = await client.rpc.system_chain();
// ...
On-chain storage can be queried via client.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: client.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 client.query.system.account(<address>);
// Get all events of current block
const events = await client.query.system.events();
Runtime constants (parameter types) are defined in metadata, and can be inspected via client.consts
entry point with format: client.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 = client.consts.system.version;
// Get existential deposit in pallet balances
const existentialDeposit = client.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 client.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 client.call.accountNonceApi.accountNonce(<address>);
// Query transaction payment info
const tx = client.tx.balances.transferKeepAlive(<address>, 2_000_000_000_000n);
const queryInfo = await client.call.transactionPaymentApi.queryInfo(tx.toU8a(), tx.length);
// Get runtime version
const runtimeVersion = await client.call.core.version();
For chains that only support Metadata V14, we need to bring in the Runtime Api definitions when initializing the DedotClient 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 client = await DedotClient.new({ provider: new WsProvider('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 client = await DedotClient.new({ provider: new WsProvider('wss://rpc.mynetwork.com'), runtimeApis: { AccountNonceApi } });
// Get account nonce
const nonce = await client.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 client.tx.balances
.transferKeepAlive(<destAddress>, 2_000_000_000_000n)
.signAndSend(alice, async ({ status }) => {
console.log('Transaction status', status.type);
if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
console.log(`Transaction completed at block hash ${status.value.blockHash}`);
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 client.tx.balances
.transferKeepAlive(<destAddress>, 2_000_000_000_000n)
.signAndSend(account.address, { signer }, async ({ status }) => {
console.log('Transaction status', status.type);
if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
console.log(`Transaction completed at block hash ${status.value.blockHash}`);
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 = client.tx.balances.transferKeepAlive(<destAddress>, 2_000_000_000_000n);
const remarkCall: PolkadotRuntimeRuntimeCallLike = {
pallet: 'System',
palletCall: {
name: 'RemarkWithEvent',
params: {
remark: 'Hello Dedot!',
},
},
};
const unsub = client.tx.utility.batch([transferTx.call, remarkCall])
.signAndSend(account.address, { signer }, async ({ status }) => {
console.log('Transaction status', status.type);
if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
console.log(`Transaction completed at block hash ${status.value.blockHash}`);
await unsub();
}
});
Example 4: Teleport WND from Westend Asset Hub to Westend via XCM
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 client = await DedotClient.new<WestendAssetHubApi>('...westend-assethub-rpc...');
const dest: XcmVersionedLocation = {
type: 'V3',
value: { parents: 1, interior: { type: 'Here' } },
};
const beneficiary: XcmVersionedLocation = {
type: 'V3',
value: {
parents: 0,
interior: {
type: 'X1',
value: {
type: 'AccountId32',
value: { id: new AccountId32(destAddress).raw },
},
},
},
};
const assets: XcmVersionedAssets = {
type: 'V3',
value: [
{
id: {
type: 'Concrete',
value: {
parents: 1,
interior: { type: 'Here' },
},
},
fun: {
type: 'Fungible',
value: TWO_TOKENS,
},
},
],
};
const weight: XcmV3WeightLimit = { type: 'Unlimited' };
client.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 client.events.<pallet>.<eventName>
. E.g: Events for Polkadot network can be found here, similarly for other network as well.
This client.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 = client.consts.system.ss58Prefix;
await client.query.system.events(async (eventRecords) => {
const newAccountEvents = client.events.system.NewAccount.filter(eventRecords);
console.log(newAccountEvents.length, 'account(s) was created in block', await client.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: client.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 client.query.system.events(async (eventRecords) => {
for (const tx of eventRecords) {
if (client.events.system.ExtrinsicFailed.is(tx.event)) {
const { dispatchError } = tx.event.palletEvent.data;
if (client.errors.assets.AlreadyExists.is(dispatchError)) {
console.log('Assets.AlreadyExists error occurred!');
} else {
console.log('Other error occurred', dispatchError);
}
}
}
});
// ...
Dedot offers type-safe APIs to interact with ink! smart contracts. Primitives to work with contracts are exposed in dedot/contract
package.
Before interacting with a contract, you need to generate Types & APIs from the contract metadata to interact with. You can do that using dedot
cli:
dedot typink -m ./path/to/metadata.json # or metadata.contract
# use option -o to customize folder to put generated types
dedot typink -m ./path/to/metadata.json -o ./where/to-put/generated-types
After running the command, Types & APIs of the contract will be generated.
E.g: if the contract's name is flipper
, the Types & APIs will be put in a folder named flipper
, the entry-point interface for the contract will be FlipperContractApi
in flipper/index.d.ts
file. An example of Types & APIs for flipper contract can be found here.
[!NOTE] If you're connecting to a local
substrate-contracts-node
for development, you might want to connect to the network usingLegacyClient
since the latest version ofsubstrate-contracts-node
(v0.41.0
) does not working fine/comply with the latest updates for new JSON-RPC specs forDedotClient
to work properly.Following this instruction to connect to the network via
LegacyClient
.
Whether it's to deploy a contract from a wasm code or using an existing wasm code hash. You can do it using the ContractDeployer
.
import { DedotClient, WsProvider } from 'dedot';
import { ContractDeployer } from 'dedot/contract';
import { stringToHex } from 'dedot/utils'
import { FlipperContractApi } from './flipper';
import flipperMetadata from './flipper.json' assert { type: 'json' };
// instanciate an api client
const client = await DedotClient.new(new WsProvider('...'));
// load contract wasm or prepare a wasm codeHash
const wasm = '0x...';
const existingCodeHash = '0x...' // uploaded wasm
// create a ContractDeployer instance
const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, wasm);
// OR from existingCodeHash
// const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, existingCodeHash);
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
// Some random salt to prevent duplication issue
// Salt is optional, you can skip this to use an empty salt
const salt = stringToHex('random-salt');
// Dry run the constructor call for validation and gas estimation
// An Error will be thrown out if there's a DispatchError or LangError (contract level error)
// More on this in the handling error section below
const dryRun = await deployer.query.new(true, { caller: ALICE, salt })
const { raw: { gasRequired } } = dryRun;
// Submitting the transaction to instanciate the contract
await deployer.tx.new(true, { gasLimit: gasRequired, salt })
.signAndSend(ALICE, ({ status, events}) => {
if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
// fully-typed event
const instantiatedEvent = client.events.contracts.Instantiated.find(events);
const contractAddress = instantiatedEvent.palletEvent.data.contract.address();
}
});
In case the contract constructor returning a Result<Self, Error>
, you can also check the see if the instantiation get any errors before submitting the transaction.
const { data } = await deployer.query.new(true, { caller: ALICE, salt })
if (data.isErr) {
console.log('Contract instantiation returning an error:', data.err);
} else {
// submitting the transaction
}
An example of this case can be found here.
The Contract
interface will be using to interact with a contract with syntax contract.query.<message>
.
import { Contract } from 'dedot/contract';
import { FlipperContractApi } from './flipper';
import flipperMetadata from './flipper.json' assert { type: 'json' };
// ... initializing DedotClient
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
const contractAddress = '...';
// create a contract instace from its metadata & address
const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
// Making call to get the current value of the flipper contract
const result = await contract.query.get({ caller: ALICE });
// Typescipt can inspect the type of value as `boolean` with the support of FlipperContractApi interface
const value: boolean = result.data;
// You can also have access to the detailed/raw result of the call
const rawResult = result.raw;
Similarly to query contracts, the Contract
interface will also be using to submitting transactions with syntax: contract.tx.<message>
import { Contract } from 'dedot/contract';
import { FlipperContractApi } from './flipper';
import flipperMetadata from './flipper.json' assert { type: 'json' };
// ... initializing DedotClient
const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
const contractAddress = '...';
// create a contract instace from its metadata & address
const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
// Dry-run the call for validation and gas estimation
const { data, raw } = await contract.query.flip({ caller: ALICE });
// Check if the message return a `Result<Data, Error>`
// Skip this check if the message returning raw Data
if (data.isErr) {
console.log('Cannot make transaction due to error:', data.err);
}
// Submitting the transaction after passing validation
await contract.tx.flip({ gasLimit: raw.gasRequired })
.signAndSend(ALICE, ({ status, events }) => {
if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
// fully-typed event
const flippedEvent = contract.events.Flipped.find(events);
console.log('Old value', flippedEvent.data.old);
console.log('New value', flippedEvent.data.new);
}
})
The Contract
interface also have APIs to help you work with contract events easily and smoothly.
import { ContractEvent } from 'dedot/contract';
// Initialize Contract instance
const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
// Extracting contract events from transaction events
await contract.tx.flip({ gasLimit: raw.gasRequired })
.signAndSend(ALICE, ({ status, events }) => {
if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
// fully-typed event
const flippedEvent = contract.events.Flipped.find(events);
console.log('Old value', flippedEvent.data.old);
console.log('New value', flippedEvent.data.new);
// an array of Flipped event
const flippedEvents = contract.events.Flipped.filter(events);
// Get all contract events from current transactions
const contractEvents: ContractEvent[] = contract.decodeEvents(events);
// Another way to get the Flipper event
const flippedEvent2 = contractEvents.find(contract.events.Flipped.is);
}
});
// Extracting contract events from system events
await client.query.system.events((events) => {
// fully-typed event
const flippedEvent = contract.events.Flipped.find(events);
// get all events of this contract from current block
const contractEvents: ContractEvent[] = contract.decodeEvents(events);
})
Interacting with a contract often resulting in errors at runtime level (DispatchError) or contract-level (LangError). Whenever running into these errors, Dedot will throw an Error containing specific context about the problem so developers can handle this accordingly.
import {
isContractInstantiateDispatchError, isContractInstantiateLangError,
isContractDispatchError, isContractLangError
} from "dedot/contracts";
import { FlipperContractApi } from "./flipper";
const ALICE = '...';
try {
// Dry-run contract construction
const dryRun = await deployer.query.new(true, { caller: ALICE })
// ...
} catch (e: any) {
if (isContractInstantiateDispatchError<FlipperContractApi>(e)) {
// Getting a runtime level error (e.g: Module error, Overflow error ...)
const { dispatchError, raw } = e;
const errorMeta = client.registy.findErrorMeta(dispatchError);
// ...
}
if (isContractInstantiateLangError<FlipperContractApi>(e)) {
const { langError, raw } = e;
console.log('LangError', langError);
}
// Other errors ...
}
try {
// Dry-run mutable contract message
const dryRun = await contract.query.flip({ caller: ALICE })
// ...
} catch (e: any) {
if (isContractDispatchError<FlipperContractApi>(e)) {
// Getting a runtime level error (e.g: Module error, Overflow error ...)
const { dispatchError, raw } = e;
const errorMeta = client.registy.findErrorMeta(dispatchError);
// ...
}
if (isContractLangError<FlipperContractApi>(e)) {
const { langError, raw } = e;
console.log('LangError', langError);
}
// Other errors ...
}
dedot
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
.
@polkadot/api
import { ApiPromise, WsProvider } from '@polkadot/api';
const client = await ApiPromise.create({ provider: new WsProvider('wss://rpc.polkadot.io') });
dedot
import { DedotClient, WsProvider } from 'dedot';
import type { PolkadotApi } from '@dedot/chaintypes';
const client = await DedotClient.new<PolkadotApi>(new WsProvider('wss://rpc.polkadot.io')); // or DedotClient.create(...) if you prefer
// OR
const client = await DedotClient.new<PolkadotApi>({ provider: new WsProvider('wss://rpc.polkadot.io') });
- Notes:
-
dedot
only supports provider can make subscription request (e.g: via Websocket). - We recommend specifying the
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 aChainApi
interface, the defaultSubstrateApi
interface will be used. -
WsProvider
fromdedot
and@polkadot/api
are different, they cannot be used interchangeable.
-
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 }
|
{ type: 'Variant1', value: number } | { type: 'Variant2', value: boolean } | { type: 'Variant2' } |
FlatEnum: enum { Variant1, Variant2 }
|
'Variant1' | 'Variant2' |
E.g 1:
const runtimeVersion = client.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 client.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 = client.consts.treasury.proposalBondMaximum.unwrapOr(undefined)?.toBigInt();
// dedot
const proposalBondMaximum: bigint | undefined = client.consts.treasury.proposalBondMaximum;
Package name | Description |
---|---|
@dedot/api | High-level abstraction apis (clients, API executors...) |
@dedot/providers | Providers for connection to JSON-RPC servers (WsProvider, SmoldotProvider) |
@dedot/types | Generic shared types across the packages |
@dedot/runtime-specs | Explicit Runtime API definitions to use for chains only supports Metadata V14 |
@dedot/shape | Basic codecs/shapes for scale-codec encode/decode |
@dedot/contracts | APIs to interact with ink! smart contracts |
@dedot/codecs | Known codecs for generic purposes ($Metadata, $AccountId32, $Extrinsic ...) |
@dedot/utils | Useful utility functions |
@dedot/storage | Storage API for different purposes (caching, ...) |
@dedot/codegen | Types & APIs generation engine for chaintypes & ink! smart contracts |
@dedot/cli | Dedot's CLI |
dedot | Umbrella package re-exporting API from other packages |
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.