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.
- package/README.md +353 -48
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dedot
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
- ✅
|
|
18
|
-
|
|
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
|
-
- ✅
|
|
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
|
|
26
|
-
- ✅ Fully-typed low-level JSON-RPC client
|
|
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
|
-
###
|
|
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
|
|
63
|
+
yarn add dedot
|
|
36
64
|
|
|
37
65
|
# via npm
|
|
38
|
-
npm i dedot
|
|
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
|
|
73
|
+
yarn add -D @dedot/chaintypes
|
|
45
74
|
|
|
46
75
|
# via npm
|
|
47
|
-
npm i -D @dedot/chaintypes
|
|
76
|
+
npm i -D @dedot/chaintypes
|
|
48
77
|
```
|
|
49
78
|
|
|
50
|
-
- Initialize
|
|
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
|
|
146
|
+
yarn add -D @dedot/chaintypes
|
|
127
147
|
|
|
128
148
|
# via npm
|
|
129
|
-
npm i -D @dedot/chaintypes
|
|
149
|
+
npm i -D @dedot/chaintypes
|
|
130
150
|
```
|
|
131
151
|
|
|
132
|
-
Initialize
|
|
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 `
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
25
|
-
"@dedot/cli": "0.
|
|
26
|
-
"@dedot/codecs": "0.
|
|
27
|
-
"@dedot/contracts": "0.
|
|
28
|
-
"@dedot/providers": "0.
|
|
29
|
-
"@dedot/runtime-specs": "0.
|
|
30
|
-
"@dedot/shape": "0.
|
|
31
|
-
"@dedot/types": "0.
|
|
32
|
-
"@dedot/utils": "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": "
|
|
95
|
+
"gitHead": "f6271b5c12d8bf909e4b1a186417bf26db504c63",
|
|
96
96
|
"module": "./index.js",
|
|
97
97
|
"types": "./index.d.ts"
|
|
98
98
|
}
|