diff --git a/docs/pages/recipes/upgrade.md b/docs/pages/recipes/upgrade.md index f00e6658..2be42d35 100644 --- a/docs/pages/recipes/upgrade.md +++ b/docs/pages/recipes/upgrade.md @@ -39,7 +39,7 @@ const nextApi = client.getTypedApi(nextDot) function performTransfer() { // check if we're running on the next version to run that first - if (await nextApi.tx.Balances.new_fancy_transfer.isCompatible()) { + if (await nextApi.tx.Balances.new_fancy_transfer.getCompatibilityLevel() >= CompatibilityLevel.BackwardsCompatible) { nextApi.tx.Balances.new_fancy_transfer({ dest: MultiAddress.Id("addr"), value: 5n, @@ -56,6 +56,6 @@ function performTransfer() { Furthermore, the runtime upgrade might happen while the dApp is running, and this will still work without needing to redo the connection. As soon as the upgrade is received, the compatible check will work as expected and the dApp will start using the next runtime. -As a note, `isCompatible` is a function available on every interaction on the typedApi (queries, apis, constants, events, transactions). If used without any parameter it will return a `Promise`, because it needs to wait for the runtime to be loaded before it can tell whether it's compatible or not. +As a note, `getCompatibilityLevel` is a function available on every interaction on the typedApi (queries, apis, constants, events, transactions). If used without any parameter it will return a `Promise`, because it needs to wait for the runtime to be loaded before it can tell whether it's compatible or not. -If you have multiple `isCompatible` checks that you don't want to wait for each one of them, you can first wait for the runtime to be loaded with `await dotApi.runtime.latest()`, and then pass this to `isCompatible` as a paramter. This will make `isCompatible` return synchronously. +If you have multiple `getCompatibilityLevel` checks that you don't want to wait for each one of them, you can first wait for the runtime to be loaded with `await dotApi.runtime.latest()`, and then pass this to `getCompatibilityLevel` as a paramter. This will make `getCompatibilityLevel` return synchronously. diff --git a/docs/pages/typed.md b/docs/pages/typed.md index 4d94f6ce..4456359b 100644 --- a/docs/pages/typed.md +++ b/docs/pages/typed.md @@ -25,27 +25,50 @@ It's an observable that holds the current runtime information for that specific All the other fields are a `Record>`. The first index defines the pallet that we're looking for, and the second one defines which query/tx/event/api/constant are we looking for inside that pallet. Let's see, one by one, what do we find inside of it! -## isCompatible +## getCompatibilityLevel -First of all, let's understand `isCompatible` field. It's under each query/tx/event/api/constant in any runtime. After generating the descriptors (see [Codegen](/codegen) section), we have a typed interface to every interaction with the chain. Nevertheless, breaking runtime upgrades might hit the runtime between developing and the runtime execution of your app. `isCompatible` enables you to check on runtime if there was a breaking upgrade that hit your particular method. +First of all, let's understand `getCompatibilityLevel` field. It's under each query/tx/event/api/constant in any runtime. After generating the descriptors (see [Codegen](/codegen) section), we have a typed interface to every interaction with the chain. Nevertheless, breaking runtime upgrades might hit the runtime between developing and the runtime execution of your app. `getCompatibilityLevel` enables you to check on runtime if there was a breaking upgrade that hit your particular method. -Let's see its interface, and an example. +The enum `CompatibilityLevel` defines 4 levels of compatibility: ```ts -interface IsCompatible { - (): Promise - (runtime: Runtime): boolean +enum CompatibilityLevel { + // No possible value from origin will be compatible with dest + Incompatible, + // Some values of origin will be compatible with dest + Partial, + // Every value from origin will be compatible with dest + BackwardsCompatible, + // Types are identical + Identical, } ``` -For example, let's use `typedApi.query.System.Number`. It's a simple query, we'll see in the next pages how to interact with it. We're only interested on `isCompatible`. +A `CompatibilityLevel.Partial` means that the operation might be compatible depending on the actual values being sent or received. For instance, `getCompatibilityLevel` for the transaction `utility.batch_all` might return a `CompatibilityLevel.Partial` if one of the transactions it takes as input was removed. In this case, the call will be compatible as long as you don't send the transaction that was removed as one of its inputs. + +Another instance of a partial compatibility case could be for instance if an optional property on a struct that's an input was made mandatory. In this case, if your dApp was always populating that field, it will still work properly, but if you had cases where you weren't setting it, then it will be incompatible. + +On the other hand, a `CompatibilityLevel.BackwardsCompatible`, means that the operation had some changes, but they are backwards compatible with the descriptors generated on dev time. In the case of `utility.batch_all`, this might happen when a new transaction is added as a possible input. In this case, there was a change, and PAPI lets you know about it with this level, but you can be sure that any transaction that you pass in as an input will still work. + +A backwards-compatible change also happens in structs. For instance, if an input struct removes one of their properties, those operations are still compatible. + +It needs the runtime and the descriptors to be loaded, so it has two overloads, one where it will wait for them to be loaded, returning a promise, or another that returns synchronously if you already have a reference to the `Runtime` object from `typedApi.runtime.latest()`. + +```ts +interface GetCompatibilityLevel { + (): Promise + (runtime: Runtime): CompatibilityLevel +} +``` + +For example, let's use `typedApi.query.System.Number`. It's a simple query, we'll see in the next pages how to interact with it. We're only interested on `getCompatibilityLevel`. ```ts const query = typedApi.query.System.Number const runtime = await typedApi.runtime.latest() // we already learnt about it! -// in this case `isCompatible` returns a Promise -if (await query.isCompatible()) { +// in this case `getCompatibilityLevel` returns a Promise +if ((await query.getCompatibilityLevel()) >= CompatibilityLevel.BackwardsCompatible) { // do your stuff, the query is compatible } else { // the call is not compatible! @@ -53,8 +76,8 @@ if (await query.isCompatible()) { } // another option would be to use the already loaded runtime -// in this case, `isCompatible` is sync, and returns a boolean -if (query.isCompatible(runtime)) { +// in this case, `getCompatibilityLevel` is sync, and returns a boolean +if (query.getCompatibilityLevel(runtime) >= CompatibilityLevel.BackwardsCompatible) { // do your stuff, the query is compatible } else { // the call is not compatible! @@ -62,6 +85,6 @@ if (query.isCompatible(runtime)) { } ``` -As you can see, `isCompatible` is really powerful since we can prepare for runtime upgrades seamlessly using PAPI. See [this recipe](/recipes/upgrade) for an example! +As you can see, `getCompatibilityLevel` is really powerful since we can prepare for runtime upgrades seamlessly using PAPI. See [this recipe](/recipes/upgrade) for an example! Let's continue with the rest of the fields! diff --git a/docs/pages/typed/apis.md b/docs/pages/typed/apis.md index 58822f85..9858d287 100644 --- a/docs/pages/typed/apis.md +++ b/docs/pages/typed/apis.md @@ -9,7 +9,7 @@ type CallOptions = Partial<{ }> interface RuntimeCall { (...args: [...Args, options?: CallOptions]): Promise - isCompatible: IsCompatible + getCompatibilityLevel: GetCompatibilityLevel } ``` diff --git a/docs/pages/typed/constants.md b/docs/pages/typed/constants.md index 0e71ad25..9ffe4a59 100644 --- a/docs/pages/typed/constants.md +++ b/docs/pages/typed/constants.md @@ -1,6 +1,6 @@ # Constants -Constants are the simplest structure that we find inside the `TypedApi`. Constants are hard-coded key-value pairs that are embedded in the runtime metadata. In PAPI their structure is just a simple function that return its decoded value, with two alternatives. As explained in [the previous section](/typed) for `isCompatible`, we have two options to get the value: +Constants are the simplest structure that we find inside the `TypedApi`. Constants are hard-coded key-value pairs that are embedded in the runtime metadata. In PAPI their structure is just a simple function that return its decoded value, with two alternatives. As explained in [the previous section](/typed#getcompatibilitylevel) for `getCompatibility Level`, we have two options to get the value: - Promise-based call, without passing the runtime - Synchronous return, passing the runtime previously awaited for diff --git a/docs/pages/typed/events.md b/docs/pages/typed/events.md index d8426fab..a77aeb38 100644 --- a/docs/pages/typed/events.md +++ b/docs/pages/typed/events.md @@ -32,11 +32,11 @@ type EvClient = { pull: EvPull watch: EvWatch filter: EvFilter - isCompatible: IsCompatible + getCompatibilityLevel: GetCompatibilityLevel } ``` -We already learnt about `isCompatible`, let's see step by step the other methods: +We already learnt about `getCompatibilityLevel`, let's see step by step the other methods: ## Pull diff --git a/docs/pages/typed/queries.md b/docs/pages/typed/queries.md index 3b2ccc6a..3c6b290b 100644 --- a/docs/pages/typed/queries.md +++ b/docs/pages/typed/queries.md @@ -13,7 +13,7 @@ type CallOptions = Partial<{ }> type StorageEntryWithoutKeys = { - isCompatible: IsCompatible + getCompatibilityLevel: GetCompatibilityLevel getValue: (options?: CallOptions) => Promise watchValue: (bestOrFinalized?: "best" | "finalized") => Observable } @@ -29,7 +29,7 @@ Similarly, we'll use the example of `System.Account` query (it returns the infor ```ts type StorageEntryWithKeys = { - isCompatible: IsCompatible + getCompatibilityLevel: GetCompatibilityLevel getValue: (...args: [...Args, options?: CallOptions]) => Promise watchValue: ( ...args: [...Args, bestOrFinalized?: "best" | "finalized"] diff --git a/docs/pages/typed/tx.md b/docs/pages/typed/tx.md index 82caacc4..95de625f 100644 --- a/docs/pages/typed/tx.md +++ b/docs/pages/typed/tx.md @@ -5,7 +5,7 @@ Preparing, signing, and broadcasting extrinsics is one of the main purposes of p ```ts interface TxEntry { (data: Arg): Transaction - isCompatible: IsCompatible + getCompatibilityLevel: GetCompatibilityLevel } type Transaction = { @@ -21,7 +21,7 @@ type Transaction = { } ``` -[We already know how `isCompatible` works](/typed#iscompatible). In order to get a `Transaction` object, we need to pass all arguments required by the extrinsic. Let's see two examples, `Balances.transfer_keep_alive` and `NominationPools.claim_payout`. +[We already know how `getCompatibilityLevel` works](/typed#getcompatibilitylevel). In order to get a `Transaction` object, we need to pass all arguments required by the extrinsic. Let's see two examples, `Balances.transfer_keep_alive` and `NominationPools.claim_payout`. The case of `claim_payout` is the simplest one, since it doesn't take any arguments. Simply as @@ -65,7 +65,7 @@ const proxyTx = typedApi.tx.Proxy.proxy({ ## `getEncodedData` -`getEncodedData`, instead, packs the call data (without signed extensions, of course!) as a SCALE-encoded blob. It requires a `Runtime` field (like `isCompatible`). You can call without it, and it'll be a `Promise`-based call, or pass the runtime and it'll answer synchronously. Let's see an example: +`getEncodedData`, instead, packs the call data (without signed extensions, of course!) as a SCALE-encoded blob. It requires a `Runtime` field (like `getCompatibilityLevel`). You can call without it, and it'll be a `Promise`-based call, or pass the runtime and it'll answer synchronously. Let's see an example: ```ts // `getEncodedData` has this interface diff --git a/vocs.config.tsx b/vocs.config.tsx index 2eb45055..aedbc6f2 100644 --- a/vocs.config.tsx +++ b/vocs.config.tsx @@ -15,6 +15,31 @@ export default defineConfig({ text: "Getting Started", link: "/getting-started", }, + { + text: "Providers", + link: "/providers", + }, + { + text: "Codegen", + link: "/codegen", + }, + { + text: "Types", + link: "/types", + }, + { + text: "Signers", + link: "/signers", + }, + { + text: "Recipes", + items: [ + { + text: "Prepare for runtime upgrade", + link: "/recipes/upgrade", + }, + ], + }, { text: "Top-level client", items: [ @@ -53,31 +78,6 @@ export default defineConfig({ }, ], }, - { - text: "Providers", - link: "/providers", - }, - { - text: "Codegen", - link: "/codegen", - }, - { - text: "Types", - link: "/types", - }, - { - text: "Signers", - link: "/signers", - }, - { - text: "Recipes", - items: [ - { - text: "Prepare for runtime upgrade", - link: "/recipes/upgrade", - }, - ], - }, { text: "Examples", items: [