dedot 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +44 -787
  2. package/package.json +11 -11
package/README.md CHANGED
@@ -18,825 +18,82 @@ Delightful JavaScript/TypeScript client for [Polkadot](https://polkadot.network/
18
18
 
19
19
  ---
20
20
 
21
+ [Dedot](https://dedot.dev) is the next-generation JavaScript client for Polkadot and Substrate-based blockchains. Designed to elevate the dapp development experience, Dedot is built & optimized to be lightweight and tree-shakable, offering precise Types & APIs suggestions for individual Substrate-based blockchains and ink! Smart Contracts. Dedot also helps dapps efficiently connect to multiple chains simultaneously as we head toward a seamless multi-chain future.
22
+
21
23
  ### Features
22
24
 
23
25
  - ✅ Small bundle size, tree-shakable (no more bn.js or wasm-blob tight dependencies)
24
26
  - ✅ Types & APIs suggestions for each individual Substrate-based blockchain
25
27
  network ([@dedot/chaintypes](https://github.com/dedotdev/chaintypes))
26
- - ✅ Familiar api style with `@polkadot/api`, [easy & fast migration!](#migration-from-polkadotapi-to-dedot)
27
- - ✅ Native [TypeScript type system](#type-system) for scale-codec
28
+ - ✅ Familiar api style with `@polkadot/api`, [easy & fast migration!](https://docs.dedot.dev/getting-started/pjs-to-dedot)
29
+ - ✅ Native [TypeScript type system](https://docs.dedot.dev/getting-started/pjs-to-dedot#type-system) for scale-codec
28
30
  - ✅ Compatible with `@polkadot/extension`-based wallets
29
31
  - ✅ Support Metadata V14, V15 (latest)
30
- - ✅ Built-in metadata caching mechanism
31
- - ✅ Build on top of both the [new](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) & legacy (
32
+ - ✅ Built-in metadata optimization ([caching](https://docs.dedot.dev/getting-started/connect-to-network#caching-metadata), [compact mode](https://github.com/dedotdev/dedot/issues/45) ⏳)
33
+ - ✅ Build on top of both the [new](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) & [legacy](https://github.com/w3f/PSPs/blob/master/PSPs/drafts/psp-6.md) (
32
34
  deprecated soon) JSON-RPC APIs
33
- - ✅ Support light clients (e.g: [smoldot](https://www.npmjs.com/package/smoldot)) (_docs coming soon_)
34
- - ✅ [Typed Contract APIs](#interact-with-ink-smart-contracts)
35
- - ✅ Fully-typed low-level [JSON-RPC client](#execute-json-rpc-methods)
36
- - ⏳ [Compact Metadata](https://github.com/dedotdev/dedot/issues/45)
37
-
38
- ### Table of contents
39
-
40
- - [Getting started](#getting-started)
41
- - [Example Dapps & Scripts](#example-dapps--scripts)
42
- - [Chain Types & APIs](#chain-types--apis)
43
- - [Execute JSON-RPC Methods](#execute-json-rpc-methods)
44
- - [Query On-chain Storage](#query-on-chain-storage)
45
- - [Constants](#constants)
46
- - [Runtime APIs](#runtime-apis)
47
- - [Submit Transactions](#transaction-apis)
48
- - [Events](#events)
49
- - [Errors](#errors)
50
- - [Interact with ink! Smart Contracts](#interact-with-ink-smart-contracts)
51
- - [`@polkadot/api` -> `dedot`](#migration-from-polkadotapi-to-dedot)
52
- - [Packages Structure](#packages-structure)
53
- - [Credit](#credit)
54
-
55
- ### Example Dapps & Scripts
56
- - Try Dedot! - https://try.dedot.dev - [Source Code](https://github.com/dedotdev/trydedot)
57
- - Tiny Url - https://link.dedot.dev - [Source Code](https://github.com/dedotdev/link)
58
- - [Simple Playground Script](https://stackblitz.com/edit/try-dedot?file=main.ts&view=editor)
59
- - [Interact with PSP22 ink! Contract](https://stackblitz.com/edit/psp22-dedot?file=main.ts&view=editor)
60
- - Add yours?
61
-
62
- ### Getting started
63
-
64
- #### Installation & connecting to network
65
- Follow the below steps to install Dedot to your project.
66
-
67
- - Install `dedot` package
68
-
35
+ - ✅ Support [light clients](https://docs.dedot.dev/getting-started/connect-to-network#initializing-dedotclient-and-interact-with-polkadot-network) (e.g: [smoldot](https://www.npmjs.com/package/smoldot))
36
+ - ✅ [Typed Contract APIs](https://docs.dedot.dev/ink-smart-contracts/intro)
37
+ - ✅ Fully-typed low-level [JSON-RPC client](https://docs.dedot.dev/clients-and-providers/clients#jsonrpcclient)
38
+
39
+ ### Documentation
40
+ Check out Dedot documentation on the website: https://dedot.dev
41
+ - [Getting started](https://docs.dedot.dev/getting-started/installation)
42
+ - [Interact with ink! smart contracts](https://docs.dedot.dev/ink-smart-contracts/intro)
43
+ - [CLI](https://docs.dedot.dev/cli)
44
+ - [Build with Dedot](https://docs.dedot.dev/help-and-faq/built-with-dedot)
45
+
46
+ ### Example
47
+ 1. Install packages
69
48
  ```shell
70
- # via yarn
71
- yarn add dedot
49
+ npm i dedot # or yarn, pnpm
72
50
 
73
- # via npm
74
- npm i dedot
75
- ```
76
-
77
- - Install `@dedot/chaintypes` package for chain types & APIs suggestion. Skip this step if you don't use TypeScript.
78
-
79
- ```shell
80
- # via yarn
81
- yarn add -D @dedot/chaintypes
82
-
83
- # via npm
84
51
  npm i -D @dedot/chaintypes
85
52
  ```
86
-
87
- - Initialize `DedotClient` and start interacting with Polkadot network
88
-
53
+ 2. Connect to the network
89
54
  ```typescript
90
- // main.ts
91
55
  import { DedotClient, WsProvider } from 'dedot';
92
56
  import type { PolkadotApi } from '@dedot/chaintypes';
93
57
 
94
- const run = async () => {
95
- const provider = new WsProvider('wss://rpc.polkadot.io');
96
- const client = await DedotClient.new<PolkadotApi>(provider);
97
-
98
- // Call rpc `state_getMetadata` to fetch raw scale-encoded metadata and decode it.
99
- const metadata = await client.rpc.state_getMetadata();
100
- console.log('Metadata:', metadata);
101
-
102
- // Query on-chain storage
103
- const balance = await client.query.system.account(<address>);
104
- console.log('Balance:', balance);
105
-
106
-
107
- // Subscribe to on-chain storage changes
108
- const unsub = await client.query.system.number((blockNumber) => {
109
- console.log(`Current block number: ${blockNumber}`);
110
- });
111
-
112
- // Get pallet constants
113
- const ss58Prefix = client.consts.system.ss58Prefix;
114
- console.log('Polkadot ss58Prefix:', ss58Prefix);
115
-
116
- // Call runtime api
117
- const pendingRewards = await client.call.nominationPoolsApi.pendingRewards(<address>)
118
- console.log('Pending rewards:', pendingRewards);
119
-
120
- // await unsub();
121
- // await client.disconnect();
122
- }
123
-
124
- run().catch(console.error);
125
- ```
126
-
127
- #### Support CommonJS (`require`)
128
-
129
- You can also import `dedot` using `require`.
130
-
131
- ```js
132
- // main.js
133
- const { DedotClient, WsProvider } = require('dedot');
134
- // ...
135
- const provider = new WsProvider('wss://rpc.polkadot.io');
136
- const client = await DedotClient.new(provider);
137
- ```
138
-
139
- #### Using `LegacyClient` to connect via legacy JSON-RPC APIs
140
-
141
- If the JSON-RPC server doesn't support [new JSON-RPC APIs](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) yet, you can connect to the network using the `LegacyClient` which build on top of the [legacy JSON-RPC APIs](https://github.com/w3f/PSPs/blob/master/PSPs/drafts/psp-6.md).
142
-
143
- ```typescript
144
- import { LegacyClient, WsProvider } from 'dedot';
145
-
146
58
  const provider = new WsProvider('wss://rpc.polkadot.io');
147
- const client = await LegacyClient.new(provider);
148
- ```
149
-
150
- > [!NOTE]
151
- > The [new JSON-RPC APIs](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) are not well implemented/unstable for RPC Nodes using Polkadot-SDK version < `1.11.0`, so one should connect to the network using `LegacyClient` in such cases. For nodes using Polkadot-SDK version >= `1.11.0`, it's recommended to use `DedotClient` to connect to the network.
152
- >
153
- > You can easily check the current node's implementation version by calling RPC `system_version`:
154
- > ```typescript
155
- > const version = await client.rpc.system_version();
156
- > ```
157
-
158
-
159
- > [!NOTE]
160
- > It's recommended to use `DedotClient` for better performance when you connect to the network using [smoldot](https://www.npmjs.com/package/smoldot) light client via [`SmoldotProvider`](https://github.com/dedotdev/dedot/blob/main/packages/providers/src/smoldot/SmoldotProvider.ts).
161
-
162
-
163
- ### Chain Types & APIs
164
-
165
- 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.
166
-
167
- Types & APIs for each Substrate-based blockchains are defined in package [`@dedot/chaintypes`](https://github.com/dedotdev/chaintypes):
168
-
169
- ```shell
170
- # via yarn
171
- yarn add -D @dedot/chaintypes
172
-
173
- # via npm
174
- npm i -D @dedot/chaintypes
175
- ```
176
-
177
- Initialize `DedotClient` instance using the `ChainApi` interface for a target chain to enable types & APIs suggestion/autocompletion for that particular chain:
178
-
179
- ```typescript
180
- import { DedotClient, WsProvider } from 'dedot';
181
- import type { PolkadotApi, KusamaApi, MoonbeamApi, AstarApi } from '@dedot/chaintypes';
182
-
183
- // ...
184
-
185
- const polkadotClient = await DedotClient.new<PolkadotApi>(new WsProvider('wss://rpc.polkadot.io'));
186
- console.log(await polkadotClient.query.babe.authorities());
187
-
188
- const kusamaClient = await DedotClient.new<KusamaApi>(new WsProvider('wss://kusama-rpc.polkadot.io'));
189
- console.log(await kusamaClient.query.society.memberCount());
190
-
191
- const moonbeamClient = await DedotClient.new<MoonbeamApi>(new WsProvider('wss://wss.api.moonbeam.network'));
192
- console.log(await moonbeamClient.query.ethereumChainId.chainId());
193
-
194
- const astarClient = await DedotClient.new<AstarApi>(new WsProvider('wss://rpc.astar.network'));
195
- console.log(await astarClient.query.dappsStaking.blockRewardAccumulator());
196
-
197
- const client = await DedotClient.new(new WsProvider('ws://localhost:9944'));
198
-
199
- // ...
200
- ```
201
-
202
- Supported `ChainApi` interfaces are defined [here](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/index.ts), you can also generate the `ChainApi` interface for the chain you want to connect with using `dedot` cli.
203
-
204
- ```shell
205
- # Generate ChainApi interface for Polkadot network via rpc endpoint: wss://rpc.polkadot.io
206
- npx dedot chaintypes -w wss://rpc.polkadot.io
207
- ```
208
-
209
- ### Execute JSON-RPC Methods
210
-
211
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/json-rpc.d.ts), similarly for other networks as well.
212
-
213
- Examples:
214
-
215
- ```typescript
216
- // Call rpc: `state_getMetadata`
217
- const metadata = await client.rpc.state_getMetadata();
218
-
219
- // Call an arbitrary rpc: `module_rpc_name` with arguments ['param1', 'param2']
220
- const result = await client.rpc.module_rpc_name('param1', 'param2');
221
- ```
222
-
223
- 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`.
224
-
225
- ```typescript
226
- import { JsonRpcClient, WsProvider } from 'dedot';
227
- import type { PolkadotApi } from '@dedot/chaintypes';
228
-
229
- const provider = new WsProvider('wss://rpc.polkadot.io');
230
- const client = await JsonRpcClient.new<PolkadotApi>(provider);
231
- const chain = await client.rpc.system_chain();
232
-
233
- // ...
234
- ```
235
-
236
- ### Query On-chain Storage
237
-
238
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/query.d.ts), similarly for other networks as well.
239
-
240
- Examples:
241
-
242
- ```typescript
243
- // Query account balance
244
- const balance = await client.query.system.account(<address>);
245
-
246
- // Get all events of current block
247
- const events = await client.query.system.events();
248
- ```
249
-
250
- ### Constants
251
-
252
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/consts.d.ts), similarly for other networks.
253
-
254
- Examples:
255
-
256
- ```typescript
257
- // Get runtime version
258
- const runtimeVersion = client.consts.system.version;
259
-
260
- // Get existential deposit in pallet balances
261
- const existentialDeposit = client.consts.balances.existentialDeposit;
262
- ```
263
-
264
- ### Runtime APIs
265
-
266
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/runtime.d.ts), similarly for other networks as well.
267
-
268
- Examples:
269
-
270
- ```typescript
271
- // Get account nonce
272
- const nonce = await client.call.accountNonceApi.accountNonce(<address>);
273
-
274
- // Query transaction payment info
275
- const tx = client.tx.balances.transferKeepAlive(<address>, 2_000_000_000_000n);
276
- const queryInfo = await client.call.transactionPaymentApi.queryInfo(tx.toU8a(), tx.length);
277
-
278
- // Get runtime version
279
- const runtimeVersion = await client.call.core.version();
280
- ```
281
-
282
- 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`](https://github.com/dedotdev/dedot/blob/fefe71cf4a04d1433841f5cfc8400a1e2a8db112/packages/runtime-specs/src/all.ts#L21-L39) package.
283
-
284
- Examples:
285
-
286
- ```typescript
287
- import { RuntimeApis } from 'dedot/runtime-specs';
288
-
289
- const client = await DedotClient.new({ provider: new WsProvider('wss://rpc.mynetwork.com'), runtimeApis: RuntimeApis });
290
-
291
- // Or bring in only the Runtime Api definition that you want to interact with
292
- import { AccountNonceApi } from 'dedot/runtime-specs';
293
- const client = await DedotClient.new({ provider: new WsProvider('wss://rpc.mynetwork.com'), runtimeApis: { AccountNonceApi } });
294
-
295
- // Get account nonce
296
- const nonce = await client.call.accountNonceApi.accountNonce(<address>);
297
- ```
298
-
299
- You absolutely can define your own Runtime Api definition if you don't find it in the [supported list](https://github.com/dedotdev/dedot/blob/fefe71cf4a04d1433841f5cfc8400a1e2a8db112/packages/runtime-specs/src/all.ts#L21-L39).
300
-
301
- ### Transaction APIs
302
-
303
- Transaction apis are designed to be compatible with [`IKeyringPair`](https://github.com/polkadot-js/api/blob/3bdf49b0428a62f16b3222b9a31bfefa43c1ca55/packages/types/src/types/interfaces.ts#L15-L21) and [`Signer`](https://github.com/polkadot-js/api/blob/3bdf49b0428a62f16b3222b9a31bfefa43c1ca55/packages/types/src/types/extrinsic.ts#L135-L150) interfaces, so you can sign the transactions with accounts created by a [`Keyring`](https://github.com/polkadot-js/common/blob/22aab4a4e62944a2cf8c885f50be2c1b842813ec/packages/keyring/src/keyring.ts#L41-L40) or from any [Polkadot{.js}-based](https://github.com/polkadot-js/extension?tab=readme-ov-file#api-interface) wallet extensions.
304
-
305
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/tx.d.ts), similarly for other networks as well.
306
-
307
- Example 1: Sign transaction with a Keying account
308
-
309
- ```typescript
310
- import { cryptoWaitReady } from '@polkadot/util-crypto';
311
- import { Keyring } from '@polkadot/keyring';
312
-
313
- // ...
314
-
315
- await cryptoWaitReady();
316
- const keyring = new Keyring({ type: 'sr25519' });
317
- const alice = keyring.addFromUri('//Alice');
318
-
319
- const unsub = await client.tx.balances
320
- .transferKeepAlive(<destAddress>, 2_000_000_000_000n)
321
- .signAndSend(alice, async ({ status }) => {
322
- console.log('Transaction status', status.type);
323
- if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
324
- console.log(`Transaction completed at block hash ${status.value.blockHash}`);
325
- await unsub();
326
- }
327
- });
328
- ```
329
-
330
- Example 2: Sign transaction using `Signer` from Polkadot{.js} wallet extension
331
-
332
- ```typescript
333
- const injected = await window.injectedWeb3['polkadot-js'].enable('A cool dapp');
334
- const account = (await injected.accounts.get())[0];
335
- const signer = injected.signer;
336
-
337
- const unsub = await client.tx.balances
338
- .transferKeepAlive(<destAddress>, 2_000_000_000_000n)
339
- .signAndSend(account.address, { signer }, async ({ status }) => {
340
- console.log('Transaction status', status.type);
341
- if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
342
- console.log(`Transaction completed at block hash ${status.value.blockHash}`);
343
- await unsub();
344
- }
345
- });
346
- ```
347
-
348
- Example 3: Submit a batch transaction
349
-
350
- ```typescript
351
- import type { PolkadotRuntimeRuntimeCallLike } from '@dedot/chaintypes/polkadot';
352
-
353
- // Omit the detail for simplicity
354
- const account = ...;
355
- const signer = ...;
356
-
357
- const transferTx = client.tx.balances.transferKeepAlive(<destAddress>, 2_000_000_000_000n);
358
- const remarkCall: PolkadotRuntimeRuntimeCallLike = {
359
- pallet: 'System',
360
- palletCall: {
361
- name: 'RemarkWithEvent',
362
- params: {
363
- remark: 'Hello Dedot!',
364
- },
365
- },
366
- };
367
-
368
- const unsub = client.tx.utility.batch([transferTx.call, remarkCall])
369
- .signAndSend(account.address, { signer }, async ({ status }) => {
370
- console.log('Transaction status', status.type);
371
- if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
372
- console.log(`Transaction completed at block hash ${status.value.blockHash}`);
373
- await unsub();
374
- }
375
- });
376
- ```
377
-
378
- <details>
379
- <summary>Example 4: Teleport WND from Westend Asset Hub to Westend via XCM</summary>
380
-
381
- ```typescript
382
- import { WestendAssetHubApi, XcmVersionedLocation, XcmVersionedAssets, XcmV3WeightLimit } from '@dedot/chaintypes/westendAssetHub';
383
- import { AccountId32 } from 'dedot/codecs';
384
-
385
- const TWO_TOKENS = 2_000_000_000_000n;
386
- const destAddress = <bobAddress>;
387
-
388
- const client = await DedotClient.new<WestendAssetHubApi>('...westend-assethub-rpc...');
389
-
390
- const dest: XcmVersionedLocation = {
391
- type: 'V3',
392
- value: { parents: 1, interior: { type: 'Here' } },
393
- };
394
-
395
- const beneficiary: XcmVersionedLocation = {
396
- type: 'V3',
397
- value: {
398
- parents: 0,
399
- interior: {
400
- type: 'X1',
401
- value: {
402
- type: 'AccountId32',
403
- value: { id: new AccountId32(destAddress).raw },
404
- },
405
- },
406
- },
407
- };
408
-
409
- const assets: XcmVersionedAssets = {
410
- type: 'V3',
411
- value: [
412
- {
413
- id: {
414
- type: 'Concrete',
415
- value: {
416
- parents: 1,
417
- interior: { type: 'Here' },
418
- },
419
- },
420
- fun: {
421
- type: 'Fungible',
422
- value: TWO_TOKENS,
423
- },
424
- },
425
- ],
426
- };
427
-
428
- const weight: XcmV3WeightLimit = { type: 'Unlimited' };
429
-
430
- client.tx.polkadotXcm
431
- .limitedTeleportAssets(dest, beneficiary, assets, 0, weight)
432
- .signAndSend(alice, { signer, tip: 1_000_000n }, (result) => {
433
- console.dir(result, { depth: null });
434
- });
435
- ```
436
-
437
- </details>
438
-
439
- ### Events
440
-
441
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/events.d.ts), similarly for other network as well.
442
-
443
- 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.
444
-
445
- Example to list new accounts created in each block:
446
-
447
- ```typescript
448
- // ...
449
- const ss58Prefix = client.consts.system.ss58Prefix;
450
- await client.query.system.events(async (eventRecords) => {
451
- const newAccountEvents = client.events.system.NewAccount.filter(eventRecords);
59
+ const client = await DedotClient.new<PolkadotApi>(provider);
452
60
 
453
- console.log(newAccountEvents.length, 'account(s) was created in block', await client.query.system.number());
61
+ // Call rpc `state_getMetadata` to fetch raw scale-encoded metadata and decode it.
62
+ const metadata = await client.rpc.state_getMetadata();
63
+ console.log('Metadata:', metadata);
454
64
 
455
- newAccountEvents.forEach((event, index) => {
456
- console.log(`New Account ${index + 1}:`, event.palletEvent.data.account.address(ss58Prefix));
457
- });
65
+ // Listen to best blocks
66
+ client.chainHead.on('bestBlock', (block: PinnedBlock) => { // or 'finalizedBlock'
67
+ console.log(`Current best block number: ${block.number}, hash: ${block.hash}`);
458
68
  });
459
- // ...
460
- ```
461
-
462
- ### Errors
463
-
464
- 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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/errors.d.ts).
465
-
466
- Similar to events API, this API is helpful when we want to check if an error maches with an error that we're expecting.
467
-
468
- Example if an error is `AlreadyExists` from `Assets` pallet:
469
-
470
- ```typescript
471
- // ...
472
- await client.query.system.events(async (eventRecords) => {
473
- for (const tx of eventRecords) {
474
- if (client.events.system.ExtrinsicFailed.is(tx.event)) {
475
- const { dispatchError } = tx.event.palletEvent.data;
476
- if (client.errors.assets.AlreadyExists.is(dispatchError)) {
477
- console.log('Assets.AlreadyExists error occurred!');
478
- } else {
479
- console.log('Other error occurred', dispatchError);
480
- }
481
- }
482
- }
483
- });
484
- // ...
485
- ```
486
-
487
- ### Interact with ink! Smart Contracts
488
- Dedot offers type-safe APIs to interact with ink! smart contracts. Primitives to work with contracts are exposed in `dedot/contract` package.
489
-
490
- #### Generate Types & APIs from contract metadata
491
- 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:
492
-
493
- ```shell
494
- dedot typink -m ./path/to/metadata.json # or metadata.contract
495
-
496
- # use option -o to customize folder to put generated types
497
- dedot typink -m ./path/to/metadata.json -o ./where/to-put/generated-types
498
- ```
499
- After running the command, Types & APIs of the contract will be generated.
500
- 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](https://github.com/dedotdev/dedot/tree/main/zombienet-tests/src/contracts/flipper).
501
-
502
- > [!NOTE]
503
- > If you're connecting to a local [`substrate-contracts-node`](https://github.com/paritytech/substrate-contracts-node/releases) for development, you might want to connect to the network using `LegacyClient` since the latest version of `substrate-contracts-node` ([`v0.41.0`](https://github.com/paritytech/substrate-contracts-node/releases/tag/v0.41.0)) does not working fine/comply with the latest updates for [new JSON-RPC specs](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) for `DedotClient` to work properly.
504
- >
505
- > Following [this instruction](#using-legacyclient-to-connect-via-legacy-json-rpc-apis) to connect to the network via `LegacyClient`.
506
-
507
- #### Deploy contracts
508
-
509
- 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`.
510
-
511
- ```typescript
512
- import { DedotClient, WsProvider } from 'dedot';
513
- import { ContractDeployer } from 'dedot/contract';
514
- import { stringToHex } from 'dedot/utils'
515
- import { FlipperContractApi } from './flipper';
516
- import flipperMetadata from './flipper.json' assert { type: 'json' };
517
-
518
- // instanciate an api client
519
- const client = await DedotClient.new(new WsProvider('...'));
520
-
521
- // load contract wasm or prepare a wasm codeHash
522
- const wasm = '0x...';
523
- const existingCodeHash = '0x...' // uploaded wasm
524
-
525
- // create a ContractDeployer instance
526
- const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, wasm);
527
-
528
- // OR from existingCodeHash
529
- // const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, existingCodeHash);
530
-
531
- const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
532
-
533
- // Some random salt to prevent duplication issue
534
- // Salt is optional, you can skip this to use an empty salt
535
- const salt = stringToHex('random-salt');
536
-
537
- // Dry run the constructor call for validation and gas estimation
538
- // An Error will be thrown out if there's a DispatchError or LangError (contract level error)
539
- // More on this in the handling error section below
540
- const dryRun = await deployer.query.new(true, { caller: ALICE, salt })
541
- const { raw: { gasRequired } } = dryRun;
542
-
543
- // Submitting the transaction to instanciate the contract
544
- await deployer.tx.new(true, { gasLimit: gasRequired, salt })
545
- .signAndSend(ALICE, ({ status, events}) => {
546
- if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
547
- // fully-typed event
548
- const instantiatedEvent = client.events.contracts.Instantiated.find(events);
549
- const contractAddress = instantiatedEvent.palletEvent.data.contract.address();
550
- }
551
- });
552
- ```
553
-
554
- 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.
555
-
556
- ```typescript
557
- const { data } = await deployer.query.new(true, { caller: ALICE, salt })
558
- if (data.isErr) {
559
- console.log('Contract instantiation returning an error:', data.err);
560
- } else {
561
- // submitting the transaction
562
- }
563
- ```
564
-
565
- An example of this case can be found [here](https://github.com/dedotdev/dedot/blob/005ac48f5dcc5259da4a20fd5e87e4990bd773b3/zombienet-tests/src/0001-verify-contract-errors.ts#L43-L44).
566
-
567
- #### Query contracts
568
-
569
- The `Contract` interface will be using to interact with a contract with syntax `contract.query.<message>`.
570
-
571
- ```typescript
572
- import { Contract } from 'dedot/contract';
573
- import { FlipperContractApi } from './flipper';
574
- import flipperMetadata from './flipper.json' assert { type: 'json' };
575
-
576
- // ... initializing DedotClient
577
-
578
- const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
579
- const contractAddress = '...';
580
-
581
- // create a contract instace from its metadata & address
582
- const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
583
-
584
- // Making call to get the current value of the flipper contract
585
- const result = await contract.query.get({ caller: ALICE });
586
-
587
- // Typescipt can inspect the type of value as `boolean` with the support of FlipperContractApi interface
588
- const value: boolean = result.data;
589
-
590
- // You can also have access to the detailed/raw result of the call
591
- const rawResult = result.raw;
592
- ```
593
-
594
- #### Submitting transactions
595
-
596
- Similarly to query contracts, the `Contract` interface will also be using to submitting transactions with syntax: `contract.tx.<message>`
597
-
598
- ```typescript
599
- import { Contract } from 'dedot/contract';
600
- import { FlipperContractApi } from './flipper';
601
- import flipperMetadata from './flipper.json' assert { type: 'json' };
602
69
 
603
- // ... initializing DedotClient
604
-
605
- const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
606
- const contractAddress = '...';
607
-
608
- // create a contract instace from its metadata & address
609
- const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
610
-
611
- // Dry-run the call for validation and gas estimation
612
- const { data, raw } = await contract.query.flip({ caller: ALICE });
613
-
614
- // Check if the message return a `Result<Data, Error>`
615
- // Skip this check if the message returning raw Data
616
- if (data.isErr) {
617
- console.log('Cannot make transaction due to error:', data.err);
618
- }
619
-
620
- // Submitting the transaction after passing validation
621
- await contract.tx.flip({ gasLimit: raw.gasRequired })
622
- .signAndSend(ALICE, ({ status, events }) => {
623
- if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
624
- // fully-typed event
625
- const flippedEvent = contract.events.Flipped.find(events);
626
- console.log('Old value', flippedEvent.data.old);
627
- console.log('New value', flippedEvent.data.new);
628
- }
629
- })
630
-
631
- ```
632
-
633
- #### Contract events
634
-
635
- The `Contract` interface also have APIs to help you work with contract events easily and smoothly.
636
-
637
- ```typescript
638
- import { ContractEvent } from 'dedot/contract';
639
-
640
- // Initialize Contract instance
641
- const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
642
-
643
- // Extracting contract events from transaction events
644
- await contract.tx.flip({ gasLimit: raw.gasRequired })
645
- .signAndSend(ALICE, ({ status, events }) => {
646
- if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
647
- // fully-typed event
648
- const flippedEvent = contract.events.Flipped.find(events);
649
- console.log('Old value', flippedEvent.data.old);
650
- console.log('New value', flippedEvent.data.new);
651
-
652
- // an array of Flipped event
653
- const flippedEvents = contract.events.Flipped.filter(events);
654
-
655
- // Get all contract events from current transactions
656
- const contractEvents: ContractEvent[] = contract.decodeEvents(events);
657
-
658
- // Another way to get the Flipper event
659
- const flippedEvent2 = contractEvents.find(contract.events.Flipped.is);
660
- }
661
- });
662
-
663
- // Extracting contract events from system events
664
- await client.query.system.events((events) => {
665
- // fully-typed event
666
- const flippedEvent = contract.events.Flipped.find(events);
667
-
668
- // get all events of this contract from current block
669
- const contractEvents: ContractEvent[] = contract.decodeEvents(events);
670
- })
671
- ```
672
-
673
- #### Handling errors
674
-
675
- Interacting with a contract often resulting in errors at runtime level ([DispatchError](https://docs.rs/frame-support/latest/frame_support/pallet_prelude/enum.DispatchError.html)) or contract-level ([LangError](https://use.ink/4.x/faq/migrating-from-ink-3-to-4#add-support-for-language-level-errors-langerror)).
676
- Whenever running into these errors, Dedot will throw an Error containing specific context about the problem so developers can handle this accordingly.
677
-
678
- ```typescript
679
- import {
680
- isContractInstantiateDispatchError, isContractInstantiateLangError,
681
- isContractDispatchError, isContractLangError
682
- } from "dedot/contracts";
683
- import { FlipperContractApi } from "./flipper";
684
-
685
- const ALICE = '...';
686
-
687
- try {
688
- // Dry-run contract construction
689
- const dryRun = await deployer.query.new(true, { caller: ALICE })
690
-
691
- // ...
692
- } catch (e: any) {
693
- if (isContractInstantiateDispatchError<FlipperContractApi>(e)) {
694
- // Getting a runtime level error (e.g: Module error, Overflow error ...)
695
- const { dispatchError, raw } = e;
696
- const errorMeta = client.registy.findErrorMeta(dispatchError);
697
- // ...
698
- }
699
-
700
- if (isContractInstantiateLangError<FlipperContractApi>(e)) {
701
- const { langError, raw } = e;
702
- console.log('LangError', langError);
703
- }
704
-
705
- // Other errors ...
706
- }
707
-
708
- try {
709
- // Dry-run mutable contract message
710
- const dryRun = await contract.query.flip({ caller: ALICE })
711
-
712
- // ...
713
- } catch (e: any) {
714
- if (isContractDispatchError<FlipperContractApi>(e)) {
715
- // Getting a runtime level error (e.g: Module error, Overflow error ...)
716
- const { dispatchError, raw } = e;
717
- const errorMeta = client.registy.findErrorMeta(dispatchError);
718
- // ...
719
- }
720
-
721
- if (isContractLangError<FlipperContractApi>(e)) {
722
- const { langError, raw } = e;
723
- console.log('LangError', langError);
724
- }
725
-
726
- // Other errors ...
727
- }
728
- ```
729
-
730
- ### Migration from `@polkadot/api` to `dedot`
731
- `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.
732
-
733
- While the api style are similar, but there're also some differences you might need to be aware of when switching to use `dedot`.
734
-
735
- #### Initialize api client
736
-
737
- - `@polkadot/api`
738
-
739
- ```typescript
740
- import { ApiPromise, WsProvider } from '@polkadot/api';
741
-
742
- const client = await ApiPromise.create({ provider: new WsProvider('wss://rpc.polkadot.io') });
743
- ```
744
-
745
- - `dedot`
746
-
747
- ```typescript
748
- import { DedotClient, WsProvider } from 'dedot';
749
- import type { PolkadotApi } from '@dedot/chaintypes';
750
-
751
- const client = await DedotClient.new<PolkadotApi>(new WsProvider('wss://rpc.polkadot.io')); // or DedotClient.create(...) if you prefer
752
-
753
- // OR
754
- const client = await DedotClient.new<PolkadotApi>({ provider: new WsProvider('wss://rpc.polkadot.io') });
755
- ```
756
-
757
- - Notes:
758
- - `dedot` only supports provider can make subscription request (e.g: via Websocket).
759
- - We recommend specifying the `ChainApi` interface (e.g: [`PolkadotApi`](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/index.d.ts) 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`](https://github.com/dedotdev/dedot/blob/a762faf8f6af40d3e4ef163bd538b270a5ca31e8/packages/chaintypes/src/substrate/index.d.ts) interface will be used.
760
- - `WsProvider` from `dedot` and `@polkadot/api` are different, they cannot be used interchangeable.
761
-
762
- #### Type system
763
-
764
- Unlike `@polkadot/api` where data are wrapped inside a [codec types](https://polkadot.js.org/docs/api/start/types.basics), 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`:
765
-
766
-
767
- | Scale Codec | TypeScript (`dedot`) |
768
- |---------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
769
- | `u8`, `u16`, `u32`, `i8`, `i16`, `i32` | `number` |
770
- | `u64`, `u128`, `u256`, `i64`, `i128`, `i256` | `bigint` (native [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt), not bn.js) |
771
- | `bool` | `boolean` (true, false) |
772
- | `Option<T>` | `T \| undefined` |
773
- | `Result<Ok, Err>` | `{ isOk: true; isErr?: false; value: Ok } \| { isOk?: false; isErr: true; err: Err }` |
774
- | `Vec<T>` | `Array<T>` |
775
- | `str` | `string` |
776
- | Tuple: `(A, B)`, `()` | `[A, B]`, `[]` |
777
- | Struct: `struct { field_1: u8, field_2: str }` | `{ field_1: number, field_2: string}` |
778
- | Enum: `enum { Variant1(u8), Variant2(bool), Variant3 }` | `{ type: 'Variant1', value: number } \| { type: 'Variant2', value: boolean } \| { type: 'Variant2' }` |
779
- | FlatEnum: `enum { Variant1, Variant2 }` | `'Variant1' \| 'Variant2'` |
780
-
781
- E.g 1:
782
-
783
- ```typescript
784
- const runtimeVersion = client.consts.system.version;
785
-
786
- // @polkadot/api
787
- const specName: string = runtimeVersion.toJSON().specName; // OR runtimeVersion.specName.toString()
788
-
789
- // dedot
790
- const specName: string = runtimeVersion.specName;
791
- ```
792
-
793
- E.g 2:
794
-
795
- ```typescript
70
+ // Query on-chain storage
796
71
  const balance = await client.query.system.account(<address>);
72
+ console.log('Balance:', balance);
797
73
 
798
- // @polkadot/api
799
- const freeBalance: bigint = balance.data.free.toBigInt();
800
-
801
- // dedot
802
- const freeBalance: bigint = balance.data.free;
803
- ```
804
-
805
- E.g 3:
74
+ // Get pallet constants
75
+ const ss58Prefix = client.consts.system.ss58Prefix;
76
+ console.log('Polkadot ss58Prefix:', ss58Prefix);
806
77
 
807
- ```typescript
808
- // @polkadot/api
809
- const proposalBondMaximum: bigint | undefined = client.consts.treasury.proposalBondMaximum.unwrapOr(undefined)?.toBigInt();
78
+ // Call runtime api
79
+ const pendingRewards = await client.call.nominationPoolsApi.pendingRewards(<address>)
80
+ console.log('Pending rewards:', pendingRewards);
810
81
 
811
- // dedot
812
- const proposalBondMaximum: bigint | undefined = client.consts.treasury.proposalBondMaximum;
82
+ // await unsub();
83
+ // await client.disconnect();
813
84
  ```
814
85
 
815
- ### Packages Structure
816
-
817
- | Package name | Description |
818
- |--------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
819
- | [@dedot/api](https://github.com/dedotdev/dedot/tree/main/packages/api) | High-level abstraction apis (clients, API executors...) |
820
- | [@dedot/providers](https://github.com/dedotdev/dedot/tree/main/packages/providers) | Providers for connection to JSON-RPC servers (WsProvider, SmoldotProvider) |
821
- | [@dedot/types](https://github.com/dedotdev/dedot/tree/main/packages/types) | Generic shared types across the packages |
822
- | [@dedot/runtime-specs](https://github.com/dedotdev/dedot/tree/main/packages/runtime-specs) | Explicit Runtime API definitions to use for chains only supports Metadata V14 |
823
- | [@dedot/shape](https://github.com/dedotdev/dedot/tree/main/packages/shape) | Basic codecs/shapes for scale-codec encode/decode |
824
- | [@dedot/contracts](https://github.com/dedotdev/dedot/tree/main/packages/contracts) | APIs to interact with ink! smart contracts |
825
- | [@dedot/codecs](https://github.com/dedotdev/dedot/tree/main/packages/codecs) | Known codecs for generic purposes ($Metadata, $AccountId32, $Extrinsic ...) |
826
- | [@dedot/utils](https://github.com/dedotdev/dedot/tree/main/packages/utils) | Useful utility functions |
827
- | [@dedot/storage](https://github.com/dedotdev/dedot/tree/main/packages/storage) | Storage API for different purposes (caching, ...) |
828
- | [@dedot/codegen](https://github.com/dedotdev/dedot/tree/main/packages/codegen) | Types & APIs generation engine for chaintypes & ink! smart contracts |
829
- | [@dedot/cli](https://github.com/dedotdev/dedot/tree/main/packages/cli) | Dedot's CLI |
830
- | [dedot](https://github.com/dedotdev/dedot/tree/main/packages/dedot) | Umbrella package re-exporting API from other packages |
831
-
86
+ ### Resources & announcements
87
+ - [Introducing Dedot](https://forum.polkadot.network/t/introducing-dedot-a-delightful-javascript-client-for-polkadot-substrate-based-blockchains/8956)
88
+ - [Type-safe APIs to interact with ink! Smart Contracts](https://forum.polkadot.network/t/type-safe-apis-to-interact-with-ink-smart-contracts-dedot/9485)
832
89
 
833
- ### Credit
90
+ ### Acknowledment
834
91
 
835
- `dedot` take a lot of inspirations from project [@polkadot/api](https://github.com/polkadot-js/api). A big thank to all the maintainers/contributors of this awesome library.
92
+ [Dedot](https://dedot.dev) take a lot of inspirations from project [@polkadot/api](https://github.com/polkadot-js/api). A big thank to all the maintainers/contributors of this awesome library.
836
93
 
837
94
  Proudly supported by Web3 Foundation Grants Program.
838
95
  <p align="left">
839
- <img width="479" src="https://user-images.githubusercontent.com/6867026/227230786-0796214a-3e3f-42af-94e9-d4122c730b62.png">
96
+ <img width="250" src="https://user-images.githubusercontent.com/6867026/227230786-0796214a-3e3f-42af-94e9-d4122c730b62.png">
840
97
  </p>
841
98
 
842
99
  ### License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dedot",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A delightful JavaScript/TypeScript client for Polkadot & Substrate",
5
5
  "author": "Thang X. Vu <thang@coongcrafts.io>",
6
6
  "homepage": "https://github.com/dedotdev/dedot",
@@ -21,15 +21,15 @@
21
21
  "clean": "rm -rf ./dist && rm -rf ./tsconfig.tsbuildinfo ./tsconfig.build.tsbuildinfo"
22
22
  },
23
23
  "dependencies": {
24
- "@dedot/api": "0.5.0",
25
- "@dedot/cli": "0.5.0",
26
- "@dedot/codecs": "0.5.0",
27
- "@dedot/contracts": "0.5.0",
28
- "@dedot/providers": "0.5.0",
29
- "@dedot/runtime-specs": "0.5.0",
30
- "@dedot/shape": "0.5.0",
31
- "@dedot/types": "0.5.0",
32
- "@dedot/utils": "0.5.0"
24
+ "@dedot/api": "0.6.0",
25
+ "@dedot/cli": "0.6.0",
26
+ "@dedot/codecs": "0.6.0",
27
+ "@dedot/contracts": "0.6.0",
28
+ "@dedot/providers": "0.6.0",
29
+ "@dedot/runtime-specs": "0.6.0",
30
+ "@dedot/shape": "0.6.0",
31
+ "@dedot/types": "0.6.0",
32
+ "@dedot/utils": "0.6.0"
33
33
  },
34
34
  "exports": {
35
35
  ".": {
@@ -92,7 +92,7 @@
92
92
  "directory": "dist"
93
93
  },
94
94
  "license": "Apache-2.0",
95
- "gitHead": "968148ade63e07644094fc033be719b320e18a18",
95
+ "gitHead": "80e64c7da483c4f5b757f6af7259aa4fdd5397fc",
96
96
  "module": "./index.js",
97
97
  "types": "./index.d.ts"
98
98
  }