dedot 0.2.0 → 0.4.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 +353 -48
  2. package/package.json +11 -11
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dedot
2
2
 
3
- A delightful JavaScript/TypeScript client for [Polkadot](https://polkadot.network/) & [Substrate](https://substrate.io/)
3
+ Delightful JavaScript/TypeScript client for [Polkadot](https://polkadot.network/) & [Substrate](https://substrate.io/)
4
4
 
5
5
  <p align="left">
6
6
  <img src="https://img.shields.io/github/license/dedotdev/dedot?style=flat-square"/>
@@ -9,45 +9,75 @@ A delightful JavaScript/TypeScript client for [Polkadot](https://polkadot.networ
9
9
  <img src="https://img.shields.io/github/package-json/v/dedotdev/dedot?filename=packages%2Fapi%2Fpackage.json&style=flat-square"/>
10
10
  </p>
11
11
 
12
- _Note: The project is still in active development phase, the information on this page might be outdated. Feel free to raise an [issue](https://github.com/dedotdev/dedot/issues/new) if you run into any problems or want to share any ideas._
13
-
14
12
  ---
13
+
15
14
  ### Features
15
+
16
16
  - ✅ Small bundle size, tree-shakable (no more bn.js or wasm-blob tight dependencies)
17
- - ✅ Built-in metadata caching mechanism
18
- - ✅ Types & APIs suggestions for each individual Substrate-based blockchain network ([@dedot/chaintypes](https://github.com/dedotdev/chaintypes))
19
- - ✅ Familiar api style with `@polkadot/api`, easy & fast migration!
20
- - ✅ Native TypeScript type system for scale-codec
17
+ - ✅ Types & APIs suggestions for each individual Substrate-based blockchain
18
+ network ([@dedot/chaintypes](https://github.com/dedotdev/chaintypes))
19
+ - ✅ Familiar api style with `@polkadot/api`, [easy & fast migration!](#migration-from-polkadotapi-to-dedot)
20
+ - ✅ Native [TypeScript type system](#type-system) for scale-codec
21
21
  - ✅ Compatible with `@polkadot/extension`-based wallets
22
22
  - ✅ Support Metadata V14, V15 (latest)
23
- - ✅ Build on top of both the [new](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) & legacy (deprecated soon) JSON-RPC APIs
23
+ - ✅ Built-in metadata caching mechanism
24
+ - ✅ Build on top of both the [new](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) & legacy (
25
+ deprecated soon) JSON-RPC APIs
24
26
  - ✅ Support light clients (e.g: [smoldot](https://www.npmjs.com/package/smoldot)) (_docs coming soon_)
25
- - ✅ Typed Contract APIs (_docs coming soon_)
26
- - ✅ Fully-typed low-level JSON-RPC client (_docs coming soon_)
27
+ - ✅ [Typed Contract APIs](#interact-with-ink-smart-contracts)
28
+ - ✅ Fully-typed low-level [JSON-RPC client](#execute-json-rpc-methods)
27
29
  - ⏳ [Compact Metadata](https://github.com/dedotdev/dedot/issues/45)
28
30
 
29
- ### Have a quick taste
31
+ ### Table of contents
32
+
33
+ - [Getting started](#getting-started)
34
+ - [Example Dapps & Scripts](#example-dapps--scripts)
35
+ - [Chain Types & APIs](#chain-types--apis)
36
+ - [Execute JSON-RPC Methods](#execute-json-rpc-methods)
37
+ - [Query On-chain Storage](#query-on-chain-storage)
38
+ - [Constants](#constants)
39
+ - [Runtime APIs](#runtime-apis)
40
+ - [Submit Transactions](#transaction-apis)
41
+ - [Events](#events)
42
+ - [Errors](#errors)
43
+ - [Interact with ink! Smart Contracts](#interact-with-ink-smart-contracts)
44
+ - [`@polkadot/api` -> `dedot`](#migration-from-polkadotapi-to-dedot)
45
+ - [Packages Structure](#packages-structure)
46
+ - [Credit](#credit)
47
+
48
+ ### Example Dapps & Scripts
49
+ - Try Dedot! - https://try.dedot.dev - [Source Code](https://github.com/dedotdev/trydedot)
50
+ - Tiny Url - https://link.dedot.dev - [Source Code](https://github.com/dedotdev/link)
51
+ - [Simple Playground Script](https://stackblitz.com/edit/try-dedot?file=main.ts&view=editor)
52
+ - [Interact with PSP22 ink! Contract](https://stackblitz.com/edit/psp22-dedot?file=main.ts&view=editor)
53
+ - Add yours?
54
+
55
+ ### Getting started
56
+
57
+ Follow the below steps to install Dedot to your project.
30
58
 
31
- Try `dedot` now on [CodeSandbox Playground](https://codesandbox.io/p/devbox/trydedot-th96cm?file=%2Fmain.ts%3A24%2C26) or follow the below steps to run it on your local environment.
32
59
  - Install `dedot` package
60
+
33
61
  ```shell
34
62
  # via yarn
35
- yarn add dedot@latest
63
+ yarn add dedot
36
64
 
37
65
  # via npm
38
- npm i dedot@latest
66
+ npm i dedot
39
67
  ```
40
68
 
41
69
  - Install `@dedot/chaintypes` package for chain types & APIs suggestion. Skip this step if you don't use TypeScript.
70
+
42
71
  ```shell
43
72
  # via yarn
44
- yarn add -D @dedot/chaintypes@latest
73
+ yarn add -D @dedot/chaintypes
45
74
 
46
75
  # via npm
47
- npm i -D @dedot/chaintypes@latest
76
+ npm i -D @dedot/chaintypes
48
77
  ```
49
78
 
50
- - Initialize the API client and start interacting with Polkadot network
79
+ - Initialize `DedotClient` and start interacting with Polkadot network
80
+
51
81
  ```typescript
52
82
  // main.ts
53
83
  import { DedotClient, WsProvider } from 'dedot';
@@ -85,6 +115,7 @@ const run = async () => {
85
115
 
86
116
  run().catch(console.error);
87
117
  ```
118
+
88
119
  - You can also import `dedot` using `require`.
89
120
 
90
121
  ```js
@@ -96,6 +127,7 @@ const api = await DedotClient.new(provider);
96
127
  ```
97
128
 
98
129
  - If the JSON-RPC server doesn't support [new](https://paritytech.github.io/json-rpc-interface-spec/introduction.html) JSON-RPC APIs yet, you can connect using the `LegacyClient` which build on top of the legacy JSON-RPC APIs.
130
+
99
131
  ```typescript
100
132
  import { LegacyClient, WsProvider } from 'dedot';
101
133
 
@@ -103,33 +135,21 @@ const provider = new WsProvider('wss://rpc.polkadot.io');
103
135
  const api = await LegacyClient.new(provider);
104
136
  ```
105
137
 
106
- ### Table of contents
107
- - [Status](#status)
108
- - [Chain Types & APIs](#chain-types--apis)
109
- - [Execute RPC Methods](#execute-rpc-methods)
110
- - [Query On-chain Storage](#query-on-chain-storage)
111
- - [Constants](#constants)
112
- - [Runtime APIs](#runtime-apis)
113
- - [Submit Transactions](#transaction-apis)
114
- - [Events](#events)
115
- - [Errors](#errors)
116
- - [`@polkadot/api` -> `dedot`](#migration-from-polkadotapi-to-dedot)
117
- - [Credit](#credit)
118
-
119
138
  ### Chain Types & APIs
120
139
 
121
140
  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.
122
141
 
123
142
  Types & APIs for each Substrate-based blockchains are defined in package [`@dedot/chaintypes`](https://github.com/dedotdev/chaintypes):
143
+
124
144
  ```shell
125
145
  # via yarn
126
- yarn add -D @dedot/chaintypes@latest
146
+ yarn add -D @dedot/chaintypes
127
147
 
128
148
  # via npm
129
- npm i -D @dedot/chaintypes@latest
149
+ npm i -D @dedot/chaintypes
130
150
  ```
131
151
 
132
- Initialize a `DedotClient` instance using the `ChainApi` interface for a target chain to enable types & APIs suggestion/autocompletion for that particular chain:
152
+ Initialize `DedotClient` instance using the `ChainApi` interface for a target chain to enable types & APIs suggestion/autocompletion for that particular chain:
133
153
 
134
154
  ```typescript
135
155
  import { DedotClient, WsProvider } from 'dedot';
@@ -161,11 +181,12 @@ Supported `ChainApi` interfaces are defined [here](https://github.com/dedotdev/c
161
181
  npx dedot chaintypes -w wss://rpc.polkadot.io
162
182
  ```
163
183
 
164
- ### Execute RPC Methods
184
+ ### Execute JSON-RPC Methods
165
185
 
166
- 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.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.
186
+ RPCs can be executed via `api.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: `api.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.
167
187
 
168
188
  Examples:
189
+
169
190
  ```typescript
170
191
  // Call rpc: `state_getMetadata`
171
192
  const metadata = await api.rpc.state_getMetadata();
@@ -174,11 +195,25 @@ const metadata = await api.rpc.state_getMetadata();
174
195
  const result = await api.rpc.module_rpc_name('param1', 'param2');
175
196
  ```
176
197
 
198
+ 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`.
199
+
200
+ ```typescript
201
+ import { JsonRpcClient, WsProvider } from 'dedot';
202
+ import type { PolkadotApi } from '@dedot/chaintypes';
203
+
204
+ const provider = new WsProvider('wss://rpc.polkadot.io');
205
+ const client = await JsonRpcClient.new<PolkadotApi>(provider);
206
+ const chain = await client.rpc.system_chain();
207
+
208
+ // ...
209
+ ```
210
+
177
211
  ### Query On-chain Storage
178
212
 
179
213
  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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/query.d.ts), similarly for other networks as well.
180
214
 
181
215
  Examples:
216
+
182
217
  ```typescript
183
218
  // Query account balance
184
219
  const balance = await api.query.system.account(<address>);
@@ -186,11 +221,13 @@ const balance = await api.query.system.account(<address>);
186
221
  // Get all events of current block
187
222
  const events = await api.query.system.events();
188
223
  ```
224
+
189
225
  ### Constants
190
226
 
191
227
  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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/consts.d.ts), similarly for other networks.
192
228
 
193
229
  Examples:
230
+
194
231
  ```typescript
195
232
  // Get runtime version
196
233
  const runtimeVersion = api.consts.system.version;
@@ -204,6 +241,7 @@ const existentialDeposit = api.consts.balances.existentialDeposit;
204
241
  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](https://github.com/dedotdev/chaintypes/blob/main/packages/chaintypes/src/polkadot/runtime.d.ts), similarly for other networks as well.
205
242
 
206
243
  Examples:
244
+
207
245
  ```typescript
208
246
  // Get account nonce
209
247
  const nonce = await api.call.accountNonceApi.accountNonce(<address>);
@@ -219,8 +257,10 @@ const runtimeVersion = await api.call.core.version();
219
257
  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.
220
258
 
221
259
  Examples:
260
+
222
261
  ```typescript
223
262
  import { RuntimeApis } from 'dedot/runtime-specs';
263
+
224
264
  const api = await DedotClient.new({ provider: new WsProvider('wss://rpc.mynetwork.com'), runtimeApis: RuntimeApis });
225
265
 
226
266
  // Or bring in only the Runtime Api definition that you want to interact with
@@ -240,10 +280,13 @@ Transaction apis are designed to be compatible with [`IKeyringPair`](https://git
240
280
  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.
241
281
 
242
282
  Example 1: Sign transaction with a Keying account
283
+
243
284
  ```typescript
244
285
  import { cryptoWaitReady } from '@polkadot/util-crypto';
245
286
  import { Keyring } from '@polkadot/keyring';
246
- ...
287
+
288
+ // ...
289
+
247
290
  await cryptoWaitReady();
248
291
  const keyring = new Keyring({ type: 'sr25519' });
249
292
  const alice = keyring.addFromUri('//Alice');
@@ -253,13 +296,14 @@ const unsub = await api.tx.balances
253
296
  .signAndSend(alice, async ({ status }) => {
254
297
  console.log('Transaction status', status.type);
255
298
  if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
256
- console.log(`Transaction completed at block hash ${status.value}`);
299
+ console.log(`Transaction completed at block hash ${status.value.blockHash}`);
257
300
  await unsub();
258
301
  }
259
302
  });
260
303
  ```
261
304
 
262
305
  Example 2: Sign transaction using `Signer` from Polkadot{.js} wallet extension
306
+
263
307
  ```typescript
264
308
  const injected = await window.injectedWeb3['polkadot-js'].enable('A cool dapp');
265
309
  const account = (await injected.accounts.get())[0];
@@ -270,13 +314,14 @@ const unsub = await api.tx.balances
270
314
  .signAndSend(account.address, { signer }, async ({ status }) => {
271
315
  console.log('Transaction status', status.type);
272
316
  if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
273
- console.log(`Transaction completed at block hash ${status.value}`);
317
+ console.log(`Transaction completed at block hash ${status.value.blockHash}`);
274
318
  await unsub();
275
319
  }
276
320
  });
277
321
  ```
278
322
 
279
323
  Example 3: Submit a batch transaction
324
+
280
325
  ```typescript
281
326
  import type { PolkadotRuntimeRuntimeCallLike } from '@dedot/chaintypes/polkadot';
282
327
 
@@ -299,7 +344,7 @@ const unsub = api.tx.utility.batch([transferTx.call, remarkCall])
299
344
  .signAndSend(account.address, { signer }, async ({ status }) => {
300
345
  console.log('Transaction status', status.type);
301
346
  if (status.type === 'BestChainBlockIncluded') { // or status.type === 'Finalized'
302
- console.log(`Transaction completed at block hash ${status.value}`);
347
+ console.log(`Transaction completed at block hash ${status.value.blockHash}`);
303
348
  await unsub();
304
349
  }
305
350
  });
@@ -363,8 +408,8 @@ api.tx.polkadotXcm
363
408
  console.dir(result, { depth: null });
364
409
  });
365
410
  ```
366
- </details>
367
411
 
412
+ </details>
368
413
 
369
414
  ### Events
370
415
 
@@ -373,13 +418,12 @@ Events for each pallet emit during runtime operations and are defined in the med
373
418
  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.
374
419
 
375
420
  Example to list new accounts created in each block:
421
+
376
422
  ```typescript
377
423
  // ...
378
424
  const ss58Prefix = api.consts.system.ss58Prefix;
379
425
  await api.query.system.events(async (eventRecords) => {
380
- const newAccountEvents = eventRecords
381
- .map(({ event }) => api.events.system.NewAccount.as(event))
382
- .filter((one) => one);
426
+ const newAccountEvents = api.events.system.NewAccount.filter(eventRecords);
383
427
 
384
428
  console.log(newAccountEvents.length, 'account(s) was created in block', await api.query.system.number());
385
429
 
@@ -397,6 +441,7 @@ Pallet errors are thrown out when things go wrong in the runtime, those are defi
397
441
  Similar to events API, this API is helpful when we want to check if an error maches with an error that we're expecting.
398
442
 
399
443
  Example if an error is `AlreadyExists` from `Assets` pallet:
444
+
400
445
  ```typescript
401
446
  // ...
402
447
  await api.query.system.events(async (eventRecords) => {
@@ -414,18 +459,259 @@ await api.query.system.events(async (eventRecords) => {
414
459
  // ...
415
460
  ```
416
461
 
462
+ ### Interact with ink! Smart Contracts
463
+ Dedot offers type-safe APIs to interact with ink! smart contracts. Primitives to work with contracts are exposed in `dedot/contract` package.
464
+
465
+ #### Generate Types & APIs from contract metadata
466
+ 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:
467
+
468
+ ```shell
469
+ dedot typink -m ./path/to/metadata.json # or metadata.contract
470
+
471
+ # use option -o to customize folder to put generated types
472
+ dedot typink -m ./path/to/metadata.json -o ./where/to-put/generated-types
473
+ ```
474
+ After running the command, Types & APIs of the contract will be generated.
475
+ 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).
476
+
477
+ #### Deploy contracts
478
+
479
+ 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`.
480
+
481
+ ```typescript
482
+ import { DedotClient, WsProvider } from 'dedot';
483
+ import { ContractDeployer } from 'dedot/contract';
484
+ import { stringToHex } from 'dedot/utils'
485
+ import { FlipperContractApi } from './flipper';
486
+ import flipperMetadata from './flipper.json' assert { type: 'json' };
487
+
488
+ // instanciate an api client
489
+ const client = await DedotClient.new(new WsProvider('...'));
490
+
491
+ // load contract wasm or prepare a wasm codeHash
492
+ const wasm = '0x...';
493
+ const existingCodeHash = '0x...' // uploaded wasm
494
+
495
+ // create a ContractDeployer instance
496
+ const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, wasm);
497
+
498
+ // OR from existingCodeHash
499
+ // const deployer = new ContractDeployer<FlipperContractApi>(client, flipperMetadata, existingCodeHash);
500
+
501
+ const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
502
+
503
+ // Some random salt to prevent duplication issue
504
+ // Salt is optional, you can skip this to use an empty salt
505
+ const salt = stringToHex('random-salt');
506
+
507
+ // Dry run the constructor call for validation and gas estimation
508
+ // An Error will be thrown out if there's a DispatchError or LangError (contract level error)
509
+ // More on this in the handling error section below
510
+ const dryRun = await deployer.query.new(true, { caller: ALICE, salt })
511
+ const { raw: { gasRequired } } = dryRun;
512
+
513
+ // Submitting the transaction to instanciate the contract
514
+ await deployer.tx.new(true, { gasLimit: gasRequired, salt })
515
+ .signAndSend(ALICE, ({ status, events}) => {
516
+ if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
517
+ // fully-typed event
518
+ const instantiatedEvent = client.events.contracts.Instantiated.find(events);
519
+ const contractAddress = instantiatedEvent.palletEvent.data.contract.address();
520
+ }
521
+ });
522
+ ```
523
+
524
+ 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.
525
+
526
+ ```typescript
527
+ const { data } = await deployer.query.new(true, { caller: ALICE, salt })
528
+ if (data.isErr) {
529
+ console.log('Contract instantiation returning an error:', data.err);
530
+ } else {
531
+ // submitting the transaction
532
+ }
533
+ ```
534
+
535
+ 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).
536
+
537
+ #### Query contracts
538
+
539
+ The `Contract` interface will be using to interact with a contract with syntax `contract.query.<message>`.
540
+
541
+ ```typescript
542
+ import { Contract } from 'dedot/contract';
543
+ import { FlipperContractApi } from './flipper';
544
+ import flipperMetadata from './flipper.json' assert { type: 'json' };
545
+
546
+ // ... initializing DedotClient
547
+
548
+ const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
549
+ const contractAddress = '...';
550
+
551
+ // create a contract instace from its metadata & address
552
+ const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
553
+
554
+ // Making call to get the current value of the flipper contract
555
+ const result = await contract.query.get({ caller: ALICE });
556
+
557
+ // Typescipt can inspect the type of value as `boolean` with the support of FlipperContractApi interface
558
+ const value: boolean = result.data;
559
+
560
+ // You can also have access to the detailed/raw result of the call
561
+ const rawResult = result.raw;
562
+ ```
563
+
564
+ #### Submitting transactions
565
+
566
+ Similarly to query contracts, the `Contract` interface will also be using to submitting transactions with syntax: `contract.tx.<message>`
567
+
568
+ ```typescript
569
+ import { Contract } from 'dedot/contract';
570
+ import { FlipperContractApi } from './flipper';
571
+ import flipperMetadata from './flipper.json' assert { type: 'json' };
572
+
573
+ // ... initializing DedotClient
574
+
575
+ const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
576
+ const contractAddress = '...';
577
+
578
+ // create a contract instace from its metadata & address
579
+ const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
580
+
581
+ // Dry-run the call for validation and gas estimation
582
+ const { data, raw } = await contract.query.flip({ caller: ALICE });
583
+
584
+ // Check if the message return a `Result<Data, Error>`
585
+ // Skip this check if the message returning raw Data
586
+ if (data.isErr) {
587
+ console.log('Cannot make transaction due to error:', data.err);
588
+ }
589
+
590
+ // Submitting the transaction after passing validation
591
+ await contract.tx.flip({ gasLimit: raw.gasRequired })
592
+ .signAndSend(ALICE, ({ status, events }) => {
593
+ if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
594
+ // fully-typed event
595
+ const flippedEvent = contract.events.Flipped.find(events);
596
+ console.log('Old value', flippedEvent.data.old);
597
+ console.log('New value', flippedEvent.data.new);
598
+ }
599
+ })
600
+
601
+ ```
602
+
603
+ #### Contract events
604
+
605
+ The `Contract` interface also have APIs to help you work with contract events easily and smoothly.
606
+
607
+ ```typescript
608
+ import { ContractEvent } from 'dedot/contract';
609
+
610
+ // Initialize Contract instance
611
+ const contract = new Contract<FlipperContractApi>(client, flipperMetadata, contractAddress);
612
+
613
+ // Extracting contract events from transaction events
614
+ await contract.tx.flip({ gasLimit: raw.gasRequired })
615
+ .signAndSend(ALICE, ({ status, events }) => {
616
+ if (status.type === 'BestChainBlockIncluded' || status.type === 'Finalized') {
617
+ // fully-typed event
618
+ const flippedEvent = contract.events.Flipped.find(events);
619
+ console.log('Old value', flippedEvent.data.old);
620
+ console.log('New value', flippedEvent.data.new);
621
+
622
+ // an array of Flipped event
623
+ const flippedEvents = contract.events.Flipped.filter(events);
624
+
625
+ // Get all contract events from current transactions
626
+ const contractEvents: ContractEvent[] = contract.decodeEvents(events);
627
+
628
+ // Another way to get the Flipper event
629
+ const flippedEvent2 = contractEvents.find(contract.events.Flipped.is);
630
+ }
631
+ });
632
+
633
+ // Extracting contract events from system events
634
+ await client.query.system.events((events) => {
635
+ // fully-typed event
636
+ const flippedEvent = contract.events.Flipped.find(events);
637
+
638
+ // get all events of this contract from current block
639
+ const contractEvents: ContractEvent[] = contract.decodeEvents(events);
640
+ })
641
+ ```
642
+
643
+ #### Handling errors
644
+
645
+ 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)).
646
+ Whenever running into these errors, Dedot will throw an Error containing specific context about the problem so developers can handle this accordingly.
647
+
648
+ ```typescript
649
+ import {
650
+ isContractInstantiateDispatchError, isContractInstantiateLangError,
651
+ isContractDispatchError, isContractLangError
652
+ } from "dedot/contracts";
653
+ import { FlipperContractApi } from "./flipper";
654
+
655
+ const ALICE = '...';
656
+
657
+ try {
658
+ // Dry-run contract construction
659
+ const dryRun = await deployer.query.new(true, { caller: ALICE })
660
+
661
+ // ...
662
+ } catch (e: any) {
663
+ if (isContractInstantiateDispatchError<FlipperContractApi>(e)) {
664
+ // Getting a runtime level error (e.g: Module error, Overflow error ...)
665
+ const { dispatchError, raw } = e;
666
+ const errorMeta = client.registy.findErrorMeta(dispatchError);
667
+ // ...
668
+ }
669
+
670
+ if (isContractInstantiateLangError<FlipperContractApi>(e)) {
671
+ const { langError, raw } = e;
672
+ console.log('LangError', langError);
673
+ }
674
+
675
+ // Other errors ...
676
+ }
677
+
678
+ try {
679
+ // Dry-run mutable contract message
680
+ const dryRun = await contract.query.flip({ caller: ALICE })
681
+
682
+ // ...
683
+ } catch (e: any) {
684
+ if (isContractDispatchError<FlipperContractApi>(e)) {
685
+ // Getting a runtime level error (e.g: Module error, Overflow error ...)
686
+ const { dispatchError, raw } = e;
687
+ const errorMeta = client.registy.findErrorMeta(dispatchError);
688
+ // ...
689
+ }
690
+
691
+ if (isContractLangError<FlipperContractApi>(e)) {
692
+ const { langError, raw } = e;
693
+ console.log('LangError', langError);
694
+ }
695
+
696
+ // Other errors ...
697
+ }
698
+ ```
699
+
417
700
  ### Migration from `@polkadot/api` to `dedot`
418
701
  `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.
419
702
 
420
703
  While the api style are similar, but there're also some differences you might need to be aware of when switching to use `dedot`.
421
704
 
422
- **Initialize api client**
705
+ #### Initialize api client
706
+
423
707
  - `@polkadot/api`
708
+
424
709
  ```typescript
425
710
  import { ApiPromise, WsProvider } from '@polkadot/api';
426
711
 
427
712
  const api = await ApiPromise.create({ provider: new WsProvider('wss://rpc.polkadot.io') });
428
713
  ```
714
+
429
715
  - `dedot`
430
716
 
431
717
  ```typescript
@@ -443,7 +729,7 @@ const api = await DedotClient.new<PolkadotApi>({ provider: new WsProvider('wss:/
443
729
  - 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.
444
730
  - `WsProvider` from `dedot` and `@polkadot/api` are different, they cannot be used interchangeable.
445
731
 
446
- **Type system**
732
+ #### Type system
447
733
 
448
734
  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`:
449
735
 
@@ -459,10 +745,11 @@ Unlike `@polkadot/api` where data are wrapped inside a [codec types](https://pol
459
745
  | `str` | `string` |
460
746
  | Tuple: `(A, B)`, `()` | `[A, B]`, `[]` |
461
747
  | Struct: `struct { field_1: u8, field_2: str }` | `{ field_1: number, field_2: string}` |
462
- | Enum: `enum { Variant1(u8), Variant2(bool), Variant3 }` | `{ type: 'Variant1', value: number } \| { type: 'Variant2', value: boolean } \| { type: 'Variant2' }` |
748
+ | Enum: `enum { Variant1(u8), Variant2(bool), Variant3 }` | `{ type: 'Variant1', value: number } \| { type: 'Variant2', value: boolean } \| { type: 'Variant2' }` |
463
749
  | FlatEnum: `enum { Variant1, Variant2 }` | `'Variant1' \| 'Variant2'` |
464
750
 
465
751
  E.g 1:
752
+
466
753
  ```typescript
467
754
  const runtimeVersion = api.consts.system.version;
468
755
 
@@ -473,7 +760,8 @@ const specName: string = runtimeVersion.toJSON().specName; // OR runtimeVersion.
473
760
  const specName: string = runtimeVersion.specName;
474
761
  ```
475
762
 
476
- E.g 2:
763
+ E.g 2:
764
+
477
765
  ```typescript
478
766
  const balance = await api.query.system.account(<address>);
479
767
 
@@ -485,6 +773,7 @@ const freeBalance: bigint = balance.data.free;
485
773
  ```
486
774
 
487
775
  E.g 3:
776
+
488
777
  ```typescript
489
778
  // @polkadot/api
490
779
  const proposalBondMaximum: bigint | undefined = api.consts.treasury.proposalBondMaximum.unwrapOr(undefined)?.toBigInt();
@@ -493,6 +782,23 @@ const proposalBondMaximum: bigint | undefined = api.consts.treasury.proposalBond
493
782
  const proposalBondMaximum: bigint | undefined = api.consts.treasury.proposalBondMaximum;
494
783
  ```
495
784
 
785
+ ### Packages Structure
786
+
787
+ | Package name | Description |
788
+ |--------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
789
+ | [@dedot/api](https://github.com/dedotdev/dedot/tree/main/packages/api) | High-level abstraction apis (clients, API executors...) |
790
+ | [@dedot/providers](https://github.com/dedotdev/dedot/tree/main/packages/providers) | Providers for connection to JSON-RPC servers (WsProvider, SmoldotProvider) |
791
+ | [@dedot/types](https://github.com/dedotdev/dedot/tree/main/packages/types) | Generic shared types across the packages |
792
+ | [@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 |
793
+ | [@dedot/shape](https://github.com/dedotdev/dedot/tree/main/packages/shape) | Basic codecs/shapes for scale-codec encode/decode |
794
+ | [@dedot/contracts](https://github.com/dedotdev/dedot/tree/main/packages/contracts) | APIs to interact with ink! smart contracts |
795
+ | [@dedot/codecs](https://github.com/dedotdev/dedot/tree/main/packages/codecs) | Known codecs for generic purposes ($Metadata, $AccountId32, $Extrinsic ...) |
796
+ | [@dedot/utils](https://github.com/dedotdev/dedot/tree/main/packages/utils) | Useful utility functions |
797
+ | [@dedot/storage](https://github.com/dedotdev/dedot/tree/main/packages/storage) | Storage API for different purposes (caching, ...) |
798
+ | [@dedot/codegen](https://github.com/dedotdev/dedot/tree/main/packages/codegen) | Types & APIs generation engine for chaintypes & ink! smart contracts |
799
+ | [@dedot/cli](https://github.com/dedotdev/dedot/tree/main/packages/cli) | Dedot's CLI |
800
+ | [dedot](https://github.com/dedotdev/dedot/tree/main/packages/dedot) | Umbrella package re-exporting API from other packages |
801
+
496
802
 
497
803
  ### Credit
498
804
 
@@ -503,7 +809,6 @@ Proudly supported by Web3 Foundation Grants Program.
503
809
  <img width="479" src="https://user-images.githubusercontent.com/6867026/227230786-0796214a-3e3f-42af-94e9-d4122c730b62.png">
504
810
  </p>
505
811
 
506
-
507
812
  ### License
508
813
 
509
814
  [Apache-2.0](https://github.com/dedotdev/dedot/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dedot",
3
- "version": "0.2.0",
3
+ "version": "0.4.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.2.0",
25
- "@dedot/cli": "0.2.0",
26
- "@dedot/codecs": "0.2.0",
27
- "@dedot/contracts": "0.2.0",
28
- "@dedot/providers": "0.2.0",
29
- "@dedot/runtime-specs": "0.2.0",
30
- "@dedot/shape": "0.2.0",
31
- "@dedot/types": "0.2.0",
32
- "@dedot/utils": "0.2.0"
24
+ "@dedot/api": "0.4.0",
25
+ "@dedot/cli": "0.4.0",
26
+ "@dedot/codecs": "0.4.0",
27
+ "@dedot/contracts": "0.4.0",
28
+ "@dedot/providers": "0.4.0",
29
+ "@dedot/runtime-specs": "0.4.0",
30
+ "@dedot/shape": "0.4.0",
31
+ "@dedot/types": "0.4.0",
32
+ "@dedot/utils": "0.4.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": "f29e8784997251518df1e33ef782f1d87207b126",
95
+ "gitHead": "f6271b5c12d8bf909e4b1a186417bf26db504c63",
96
96
  "module": "./index.js",
97
97
  "types": "./index.d.ts"
98
98
  }