├── .github ├── release-drafter.yml └── workflows │ ├── check-lables.yml │ ├── ci.yml │ └── draft-release.yml ├── .gitignore ├── .taplo.toml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── about.hbs ├── about.toml ├── api-client ├── Cargo.toml └── src │ ├── api │ ├── api_client.rs │ ├── error.rs │ ├── mod.rs │ ├── rpc_api │ │ ├── author.rs │ │ ├── chain.rs │ │ ├── events.rs │ │ ├── frame_system.rs │ │ ├── mod.rs │ │ ├── pallet_balances.rs │ │ ├── pallet_transaction_payment.rs │ │ ├── runtime_update.rs │ │ └── state.rs │ └── runtime_api │ │ ├── account_nonce.rs │ │ ├── api_core.rs │ │ ├── authority_discovery.rs │ │ ├── block_builder.rs │ │ ├── metadata.rs │ │ ├── mmr.rs │ │ ├── mod.rs │ │ ├── session_keys.rs │ │ ├── staking.rs │ │ ├── transaction_payment.rs │ │ └── transaction_payment_call.rs │ ├── extrinsic │ ├── balances.rs │ ├── contracts.rs │ ├── mod.rs │ ├── offline_extrinsic.rs │ ├── staking.rs │ └── utility.rs │ ├── lib.rs │ └── rpc │ ├── error.rs │ ├── helpers.rs │ ├── jsonrpsee_client │ ├── mod.rs │ └── subscription.rs │ ├── mocks.rs │ ├── mod.rs │ ├── tungstenite_client │ ├── client.rs │ ├── mod.rs │ └── subscription.rs │ └── ws_client │ ├── client.rs │ ├── mod.rs │ └── subscription.rs ├── compose-macros ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── rpc.rs ├── docs ├── README.md └── overview_code_structure.svg ├── examples ├── async │ ├── Cargo.toml │ └── examples │ │ ├── benchmark_bulk_xt.rs │ │ ├── check_extrinsic_events.rs │ │ ├── compose_extrinsic.rs │ │ ├── custom_nonce.rs │ │ ├── get_blocks.rs │ │ ├── get_storage.rs │ │ ├── minimal_template_runtime.compact.compressed.wasm │ │ ├── new_json_rpc_api_calls.rs │ │ ├── print_metadata.rs │ │ ├── query_runtime_api.rs │ │ ├── runtime_update_async.rs │ │ ├── staking_batch_payout_untested.rs │ │ ├── subscribe_events.rs │ │ └── sudo.rs ├── sync │ ├── Cargo.toml │ └── examples │ │ ├── minimal_template_runtime.compact.compressed.wasm │ │ ├── runtime_update_sync.rs │ │ └── transfer_with_tungstenite_client.rs └── wasm │ ├── Cargo.toml │ └── examples │ └── wasm_example.rs ├── keystore ├── Cargo.toml ├── README.md └── src │ ├── keystore_ext.rs │ └── lib.rs ├── ksm_metadata_v14.bin ├── license_header_scs.txt ├── node-api ├── Cargo.toml ├── README.md └── src │ ├── error │ ├── dispatch_error.rs │ └── mod.rs │ ├── events │ ├── event_details.rs │ ├── mod.rs │ └── raw_event_details.rs │ ├── lib.rs │ ├── metadata │ ├── error.rs │ ├── from_v14_to_v15.rs │ ├── metadata_types.rs │ ├── mod.rs │ ├── print_metadata.rs │ └── variant_index.rs │ ├── storage.rs │ └── test_utils.rs ├── primitives ├── Cargo.toml ├── README.md └── src │ ├── config │ ├── asset_runtime_config.rs │ ├── default_runtime_config.rs │ ├── mod.rs │ └── rococo_runtime_config.rs │ ├── extrinsics │ ├── extrinsic_params.rs │ ├── extrinsic_params_without_hash_check.rs │ ├── extrinsic_v4.rs │ ├── mod.rs │ └── signer.rs │ ├── lib.rs │ ├── rpc_numbers.rs │ ├── rpc_params.rs │ └── types.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── test-no-std ├── Cargo.toml ├── README.md └── src │ └── main.rs └── testing ├── async ├── Cargo.toml └── examples │ ├── author_tests.rs │ ├── chain_tests.rs │ ├── dispatch_errors_tests.rs │ ├── dump_metadata.rs │ ├── events_tests.rs │ ├── frame_system_tests.rs │ ├── jsonrpsee_tests.rs │ ├── pallet_balances_tests.rs │ ├── pallet_transaction_payment_tests.rs │ ├── runtime_api_tests.rs │ └── state_tests.rs └── sync ├── Cargo.toml └── examples ├── keystore_tests.rs └── tungstenite_client_test.rs /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | exclude-labels: 4 | - 'E0-silent' 5 | categories: 6 | - title: '⚡ Breaking API changes' 7 | labels: 8 | - 'E2-breaksapi' 9 | - title: ' 🌈 Features' 10 | labels: 11 | - 'F6-optimization' 12 | - 'F8-newfeature' 13 | - 'F7-enhancement' 14 | - title: '🐛 Bug Fixes' 15 | labels: 16 | - 'F1-security' 17 | - 'F2-bug' 18 | - title: ' Miscellaneous ' 19 | collapse-after: 5 20 | label: 21 | - 'E1-breaksnothing' 22 | - 'E2-breaksapi' 23 | change-template: '- $TITLE (#$NUMBER) @$AUTHOR ' 24 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 25 | template: | 26 | ## What's Changed since $PREVIOUS_TAG 27 | 28 | $CHANGES 29 | -------------------------------------------------------------------------------- /.github/workflows/check-lables.yml: -------------------------------------------------------------------------------- 1 | # File is copy-pasted from Integritee worker: 2 | # https://github.com/integritee-network/worker/blob/1a58d6a625fa76935902f16c798efa453bcef9a4/.github/workflows/label-checker.yml 3 | 4 | name: Check labels 5 | on: 6 | pull_request: 7 | types: [opened, labeled, unlabeled, synchronize, ready_for_review] 8 | 9 | jobs: 10 | check_for_matching_labels: 11 | runs-on: ubuntu-latest 12 | if: github.base_ref == 'master' && github.event.pull_request.draft == false 13 | steps: 14 | - name: E Label check 15 | env: 16 | enforced_labels: "E0-silent,E1-breaksnothing,E2-breaksapi" 17 | run: | 18 | MATCH=$(jq -cn '${{ toJSON(github.event.pull_request.labels.*.name) }} as $USER_LABELS | 19 | ${{ toJSON(env.enforced_labels) }} | split(",") as $LABELS | 20 | $USER_LABELS - ($USER_LABELS - $LABELS)') 21 | if [[ "$MATCH" == '[]' ]]; then 22 | exit 1 23 | fi 24 | - name: F Label check 25 | env: 26 | enforced_labels: "F0-miscellaneous,F1-security,F2-bug,F3-test,F4-documentation,F5-refactor,F6-optimization,F7-enhancement,F8-newfeature,F9-dependencies" 27 | run: | 28 | MATCH=$(jq -cn '${{ toJSON(github.event.pull_request.labels.*.name) }} as $USER_LABELS | 29 | ${{ toJSON(env.enforced_labels) }} | split(",") as $LABELS | 30 | $USER_LABELS - ($USER_LABELS - $LABELS)') 31 | if [[ "$MATCH" == '[]' ]]; then 32 | exit 1 33 | fi 34 | -------------------------------------------------------------------------------- /.github/workflows/draft-release.yml: -------------------------------------------------------------------------------- 1 | name: Release - Publish Draft 2 | 3 | on: 4 | push: 5 | tags: 6 | # Catches v1.2.3 and v1.2.3-rc1 7 | - v[0-9]+.[0-9]+.[0-9]+* 8 | jobs: 9 | update_release_draft: 10 | permissions: 11 | # write permission is required to create a github release 12 | contents: write 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Drafts your next Release notes as Pull Requests are merged into "master" 16 | - uses: release-drafter/release-drafter@v5 17 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 18 | with: 19 | tag: ${{ github.ref_name }} 20 | name: ${{ github.ref_name }} 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | 10 | #Added by cargo 11 | # 12 | #already existing elements are commented out 13 | 14 | # IntelliJ 15 | **/.idea/ 16 | *.iml 17 | 18 | 19 | # VScode 20 | **/.vscode 21 | -------------------------------------------------------------------------------- /.taplo.toml: -------------------------------------------------------------------------------- 1 | include = ["**/Cargo.toml"] 2 | 3 | [formatting] 4 | array_auto_expand = false 5 | array_auto_collapse = false 6 | indent_string = " " 7 | inline_table_expand = false 8 | 9 | [[rule]] 10 | include = ["**/Cargo.toml"] 11 | keys = ["dependencies", "target", "patch"] 12 | 13 | [rule.formatting] 14 | reorder_keys = true 15 | 16 | [[rule]] 17 | include = ["**/Cargo.toml"] 18 | keys = ["features"] 19 | 20 | [rule.formatting] 21 | array_auto_expand = true 22 | 23 | [[rule]] 24 | keys = ["package"] 25 | formatting.reorder_keys = false -------------------------------------------------------------------------------- /about.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 36 | 37 | 38 | 39 |
40 |
41 |

Third Party Licenses

42 |

This page lists the licenses of the projects used in 'Substrate Api Client' repository.

43 |
44 | 45 |

Overview of licenses:

46 | 51 | 52 |

All license text:

53 | 67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /about.toml: -------------------------------------------------------------------------------- 1 | accepted = [ 2 | "Apache-2.0", 3 | "MIT", 4 | "Apache-2.0 WITH LLVM-exception", 5 | "BSD-2-Clause", 6 | "CC0-1.0", 7 | "BSD-3-Clause", 8 | "CDLA-Permissive-2.0", 9 | "ISC", 10 | "OpenSSL", 11 | "Unicode-DFS-2016", 12 | "Unicode-3.0", 13 | "NOASSERTION", 14 | "Zlib", 15 | "GPL-3.0", 16 | "GPL-3.0 WITH Classpath-exception-2.0" 17 | ] 18 | ignore-dev-dependencies = true 19 | ignore-build-dependencies = true 20 | workarounds = [ 21 | "ring", 22 | ] 23 | -------------------------------------------------------------------------------- /api-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "substrate-api-client" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/scs/substrate-api-client" 8 | description = "Json-rpc client with helper functions compatible with any Substrate node" 9 | readme = "../README.md" 10 | keywords = ["json", "rpc", "polkadot", "api", "blockchain"] 11 | categories = ["no-std", "wasm"] 12 | 13 | 14 | [dependencies] 15 | # crates.io no_std 16 | async-trait = { workspace = true } 17 | codec = { workspace = true } 18 | derive_more = { workspace = true } 19 | frame-metadata = { workspace = true } 20 | futures-util = { workspace = true } 21 | hex = { workspace = true } 22 | log = { workspace = true } 23 | maybe-async = { workspace = true } 24 | serde = { workspace = true } 25 | serde_json = { workspace = true } 26 | 27 | 28 | # crates.io std only 29 | url = { workspace = true, optional = true } 30 | 31 | # websocket dependent features 32 | jsonrpsee = { workspace = true, optional = true, features = ["async-client", "client-ws-transport-tls", "jsonrpsee-types"] } 33 | tungstenite = { workspace = true, optional = true, features = ["native-tls", "url"] } 34 | ws = { workspace = true, optional = true, features = ["ssl"] } 35 | 36 | # Substrate no_std dependencies 37 | sp-core = { workspace = true, features = ["full_crypto", "serde"] } 38 | sp-crypto-hashing = { workspace = true } 39 | sp-inherents = { workspace = true } 40 | sp-runtime = { workspace = true, features = ["serde"] } 41 | sp-runtime-interface = { workspace = true } 42 | sp-storage = { workspace = true, features = ["serde"] } 43 | sp-version = { workspace = true, features = ["serde"] } 44 | 45 | # substrate std / wasm only 46 | frame-support = { workspace = true, optional = true } 47 | 48 | # local deps 49 | ac-compose-macros = { workspace = true } 50 | ac-node-api = { workspace = true } 51 | ac-primitives = { workspace = true } 52 | 53 | 54 | [dev-dependencies] 55 | ac-node-api = { workspace = true, features = ["mocks"] } 56 | rococo-runtime = { workspace = true } 57 | scale-info = { workspace = true, features = ["derive"] } 58 | test-case = { workspace = true } 59 | 60 | [features] 61 | default = ["std", "jsonrpsee-client"] 62 | # To support `no_std` builds in non-32 bit environments. 63 | disable_target_static_assertions = [ 64 | "sp-runtime-interface/disable_target_static_assertions", 65 | ] 66 | 67 | # If this is active all the code compiles in synchronous mode. If not selected, code will compile to async mode. 68 | sync-api = ["ac-compose-macros/sync-api", "maybe-async/is_sync"] 69 | 70 | # Use the `jsonrpsee` crate for websocket communication. Does only provide async support and needs a tokio runtime. 71 | # Provides convenience functions such as subscription callbacks. 72 | # Most examples use the `jsonrpsee` feature and can be used for reference. 73 | jsonrpsee-client = ["std", "dep:jsonrpsee"] 74 | 75 | # Use the `tungstenite` crate for websocket communication. No async support but has some reconnection capabilities. 76 | # See the example `transfer_with_tungstenite_client` on how to use it. 77 | tungstenite-client = ["std", "tungstenite", "sync-api"] 78 | 79 | # Use the `ws` crate for websocket communication. No async support. 80 | # Establishes a new connection for each request and therefore is limited in terms of performance. 81 | # See the example `transfer_with_ws_client` on how to use it. 82 | ws-client = ["std", "ws", "sync-api"] 83 | 84 | # Enables functionality that helps to create extrinsics for `pallet-staking`. 85 | # See the `StakingExtrinsics` trait and the `staking_batch_payout` example to get an understanding 86 | # of the functionality this feature provides 87 | staking-xt = ["std", "ac-primitives/staking-xt"] 88 | 89 | # Enables functionality that helps to create extrinsics for `pallet-contracts`. 90 | # See the `ContractsExtrinsics` trait and the `contract_instantiate_with_code` example to get an understanding 91 | # of the functionality this feature provides. 92 | contracts-xt = ["std", "ac-primitives/contracts-xt"] 93 | 94 | # Enables all std features of dependencies in case of std build. 95 | std = [ 96 | # crates.io no_std 97 | "codec/std", 98 | "frame-metadata/std", 99 | "hex/std", 100 | "log/std", 101 | "serde/std", 102 | "serde_json/std", 103 | "futures-util/std", 104 | # crates.io std only 105 | "url", 106 | # substrate no_std 107 | "sp-core/std", 108 | "sp-runtime/std", 109 | "sp-runtime-interface/std", 110 | "sp-storage/std", 111 | "sp-version/std", 112 | # substrate std 113 | "frame-support", 114 | # local deps 115 | "ac-compose-macros/std", 116 | "ac-node-api/std", 117 | "ac-primitives/std", 118 | ] 119 | -------------------------------------------------------------------------------- /api-client/src/api/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use crate::{api::UnexpectedTxStatus, rpc::Error as RpcClientError, ExtrinsicReport}; 19 | use ac_node_api::{ 20 | error::DispatchError, 21 | metadata::{MetadataConversionError, MetadataError}, 22 | }; 23 | use alloc::{boxed::Box, vec::Vec}; 24 | use codec::{Decode, Encode}; 25 | use core::error::Error as ErrorT; 26 | 27 | pub type Result = core::result::Result; 28 | 29 | #[derive(Debug, derive_more::From)] 30 | pub enum Error { 31 | /// Could not fetch the genesis hash from node. 32 | FetchGenesisHash, 33 | /// Expected a signer, but none is assigned. 34 | NoSigner, 35 | /// Rpc Client Error. 36 | RpcClient(RpcClientError), 37 | /// Metadata Error. 38 | Metadata(MetadataError), 39 | /// Invalid Metadata Error. 40 | InvalidMetadata(MetadataConversionError), 41 | /// Node Api Error. 42 | NodeApi(ac_node_api::error::Error), 43 | /// Encode / Decode Error. 44 | Codec(codec::Error), 45 | /// Could not convert NumberOrHex with try_from. 46 | TryFromIntError, 47 | /// Extrinsic failed onchain. Contains the encoded report and the associated dispatch error. 48 | FailedExtrinsic(FailedExtrinsicError), 49 | /// Encountered unexpected tx status during watch process. 50 | UnexpectedTxStatus(UnexpectedTxStatus), 51 | /// Could not send update because the Stream has been closed unexpectedly. 52 | NoStream, 53 | /// Could not find the expected extrinsic. 54 | ExtrinsicNotFound, 55 | /// Could not find the expected block hash. 56 | BlockHashNotFound, 57 | /// Could not find the expected block. 58 | BlockNotFound, 59 | /// Operation needs events but events are missing. 60 | EventsMissing, 61 | /// Operation wants to add events but they are already present. 62 | EventsAlreadyPresent, 63 | /// Any custom Error. 64 | Other(Box), 65 | } 66 | 67 | /// Encountered unexpected tx status during watch process or the extrinsic failed. 68 | #[derive(Debug)] 69 | pub struct FailedExtrinsicError { 70 | dispatch_error: DispatchError, 71 | encoded_report: Vec, 72 | } 73 | 74 | impl FailedExtrinsicError { 75 | pub fn new(dispatch_error: DispatchError, encoded_report: Vec) -> Self { 76 | Self { dispatch_error, encoded_report } 77 | } 78 | 79 | pub fn dispatch_error(&self) -> &DispatchError { 80 | &self.dispatch_error 81 | } 82 | 83 | pub fn get_report(&self) -> Result> { 84 | let report = Decode::decode(&mut self.encoded_report.as_slice())?; 85 | Ok(report) 86 | } 87 | 88 | pub fn encoded_report(&self) -> &[u8] { 89 | &self.encoded_report 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /api-client/src/api/rpc_api/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | pub use self::{ 15 | author::*, chain::*, events::*, frame_system::*, pallet_balances::*, 16 | pallet_transaction_payment::*, runtime_update::*, state::*, 17 | }; 18 | 19 | pub mod author; 20 | pub mod chain; 21 | pub mod events; 22 | pub mod frame_system; 23 | pub mod pallet_balances; 24 | pub mod pallet_transaction_payment; 25 | pub mod runtime_update; 26 | pub mod state; 27 | -------------------------------------------------------------------------------- /api-client/src/api/rpc_api/pallet_balances.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | use crate::{ 14 | api::{Api, GetStorage, Result}, 15 | rpc::Request, 16 | }; 17 | use ac_primitives::config::Config; 18 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 19 | use alloc::boxed::Box; 20 | 21 | /// Interface to common calls of the substrate balances pallet. 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait GetBalance { 24 | type Balance; 25 | 26 | async fn get_existential_deposit(&self) -> Result; 27 | } 28 | 29 | #[maybe_async::maybe_async(?Send)] 30 | impl GetBalance for Api 31 | where 32 | T: Config, 33 | Client: Request, 34 | { 35 | type Balance = T::Balance; 36 | 37 | async fn get_existential_deposit(&self) -> Result { 38 | self.get_constant("Balances", "ExistentialDeposit").await 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /api-client/src/api/rpc_api/pallet_transaction_payment.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | use crate::{ 14 | api::{Api, Error, Result}, 15 | rpc::Request, 16 | }; 17 | use ac_compose_macros::rpc_params; 18 | use ac_primitives::{config::Config, FeeDetails, InclusionFee, NumberOrHex, RuntimeDispatchInfo}; 19 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 20 | use alloc::boxed::Box; 21 | use core::str::FromStr; 22 | use sp_core::Bytes; 23 | /// Interface to common calls of the substrate transaction payment pallet. 24 | #[maybe_async::maybe_async(?Send)] 25 | pub trait GetTransactionPayment { 26 | type Hash; 27 | type Balance; 28 | 29 | async fn get_fee_details( 30 | &self, 31 | encoded_extrinsic: &Bytes, 32 | at_block: Option, 33 | ) -> Result>>; 34 | 35 | async fn get_payment_info( 36 | &self, 37 | encoded_extrinsic: &Bytes, 38 | at_block: Option, 39 | ) -> Result>>; 40 | } 41 | 42 | #[maybe_async::maybe_async(?Send)] 43 | impl GetTransactionPayment for Api 44 | where 45 | T: Config, 46 | Client: Request, 47 | T::Balance: TryFrom + FromStr, 48 | { 49 | type Hash = T::Hash; 50 | type Balance = T::Balance; 51 | 52 | async fn get_fee_details( 53 | &self, 54 | encoded_extrinsic: &Bytes, 55 | at_block: Option, 56 | ) -> Result>> { 57 | let details: Option> = self 58 | .client() 59 | .request("payment_queryFeeDetails", rpc_params![encoded_extrinsic, at_block]) 60 | .await?; 61 | 62 | let details = match details { 63 | Some(details) => Some(convert_fee_details(details)?), 64 | None => None, 65 | }; 66 | Ok(details) 67 | } 68 | 69 | async fn get_payment_info( 70 | &self, 71 | encoded_extrinsic: &Bytes, 72 | at_block: Option, 73 | ) -> Result>> { 74 | let res = self 75 | .client() 76 | .request("payment_queryInfo", rpc_params![encoded_extrinsic, at_block]) 77 | .await?; 78 | Ok(res) 79 | } 80 | } 81 | 82 | fn convert_fee_details>( 83 | details: FeeDetails, 84 | ) -> Result> { 85 | let inclusion_fee = if let Some(inclusion_fee) = details.inclusion_fee { 86 | Some(inclusion_fee_with_balance(inclusion_fee)?) 87 | } else { 88 | None 89 | }; 90 | let tip = details.tip.try_into().map_err(|_| Error::TryFromIntError)?; 91 | Ok(FeeDetails { inclusion_fee, tip }) 92 | } 93 | 94 | fn inclusion_fee_with_balance>( 95 | inclusion_fee: InclusionFee, 96 | ) -> Result> { 97 | Ok(InclusionFee { 98 | base_fee: inclusion_fee.base_fee.try_into().map_err(|_| Error::TryFromIntError)?, 99 | len_fee: inclusion_fee.len_fee.try_into().map_err(|_| Error::TryFromIntError)?, 100 | adjusted_weight_fee: inclusion_fee 101 | .adjusted_weight_fee 102 | .try_into() 103 | .map_err(|_| Error::TryFromIntError)?, 104 | }) 105 | } 106 | -------------------------------------------------------------------------------- /api-client/src/api/rpc_api/runtime_update.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | use crate::{api::Error, rpc::Subscribe, rpc_api::EventSubscriptionFor, Result}; 14 | use alloc::sync::Arc; 15 | use codec::{Decode, Encode}; 16 | use core::sync::atomic::{AtomicBool, Ordering}; 17 | use serde::de::DeserializeOwned; 18 | 19 | /// Struct to support waiting for runtime updates. 20 | pub struct RuntimeUpdateDetector 21 | where 22 | Hash: DeserializeOwned + Copy + Decode, 23 | Client: Subscribe, 24 | { 25 | subscription: EventSubscriptionFor, 26 | external_cancellation: Option>, 27 | } 28 | 29 | impl RuntimeUpdateDetector 30 | where 31 | Hash: DeserializeOwned + Copy + Encode + Decode, 32 | Client: Subscribe, 33 | { 34 | pub fn new(subscription: EventSubscriptionFor) -> Self { 35 | Self { subscription, external_cancellation: None } 36 | } 37 | 38 | /// Provide the `RuntimeUpdateDetector` with the additional option to cancel the waiting 39 | /// from the outside. 40 | pub fn new_with_cancellation( 41 | subscription: EventSubscriptionFor, 42 | cancellation: Arc, 43 | ) -> Self { 44 | Self { subscription, external_cancellation: Some(cancellation) } 45 | } 46 | 47 | /// Returns true if a runtime update was detected, false if the wait was cancelled 48 | /// If not cancelled, this method only returns/resolves once a runtime update is detected. 49 | #[maybe_async::maybe_async(?Send)] 50 | pub async fn detect_runtime_update(&mut self) -> Result { 51 | 'outer: loop { 52 | if let Some(canceled) = &self.external_cancellation { 53 | if canceled.load(Ordering::SeqCst) { 54 | return Ok(false) 55 | } 56 | } 57 | let event_records = self 58 | .subscription 59 | .next_events_from_metadata() 60 | .await 61 | .ok_or(Error::Other("Error receiving events".into()))??; 62 | let event_iter = event_records.iter(); 63 | for event in event_iter { 64 | if event?.is_code_update() { 65 | break 'outer 66 | } 67 | } 68 | } 69 | Ok(true) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/account_nonce.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::config::Config; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::vec; 20 | use sp_core::Encode; 21 | 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait AccountNonceApi: RuntimeApi { 24 | type Index; 25 | type AccountId; 26 | 27 | /// The API to query account nonce (aka transaction index). 28 | async fn account_nonce( 29 | &self, 30 | account_id: Self::AccountId, 31 | at_block: Option, 32 | ) -> Result; 33 | } 34 | 35 | #[maybe_async::maybe_async(?Send)] 36 | impl AccountNonceApi for RuntimeApiClient 37 | where 38 | T: Config, 39 | Client: Request, 40 | { 41 | type Index = T::Index; 42 | type AccountId = T::AccountId; 43 | 44 | async fn account_nonce( 45 | &self, 46 | account_id: Self::AccountId, 47 | at_block: Option, 48 | ) -> Result { 49 | self.runtime_call("AccountNonceApi_account_nonce", vec![account_id.encode()], at_block) 50 | .await 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/api_core.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::{config::Config, RuntimeVersion}; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::vec; 20 | use sp_core::{Bytes, Encode}; 21 | 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait CoreApi: RuntimeApi { 24 | type Block; 25 | type Header; 26 | type RuntimeVersion; 27 | 28 | /// Execute the given block. 29 | async fn execute_block(&self, block: Self::Block, at_block: Option) -> Result<()>; 30 | 31 | /// Execute the given opaque block. 32 | async fn execute_opaque_block(&self, block: Bytes, at_block: Option) -> Result<()>; 33 | 34 | /// Initialize a block with the given header. 35 | async fn initialize_block( 36 | &self, 37 | header: Self::Header, 38 | at_block: Option, 39 | ) -> Result<()>; 40 | 41 | /// Returns the version of the runtime. 42 | async fn version(&self, at_block: Option) -> Result; 43 | } 44 | 45 | #[maybe_async::maybe_async(?Send)] 46 | impl CoreApi for RuntimeApiClient 47 | where 48 | T: Config, 49 | Client: Request, 50 | { 51 | type Block = T::Block; 52 | type Header = T::Header; 53 | type RuntimeVersion = RuntimeVersion; 54 | 55 | async fn execute_block(&self, block: Self::Block, at_block: Option) -> Result<()> { 56 | self.execute_opaque_block(block.encode().into(), at_block).await 57 | } 58 | 59 | async fn execute_opaque_block(&self, block: Bytes, at_block: Option) -> Result<()> { 60 | self.runtime_call("Core_execute_block", vec![block.0], at_block).await 61 | } 62 | 63 | async fn initialize_block( 64 | &self, 65 | header: Self::Header, 66 | at_block: Option, 67 | ) -> Result<()> { 68 | self.runtime_call("Core_initialize_block", vec![header.encode()], at_block) 69 | .await 70 | } 71 | 72 | async fn version(&self, at_block: Option) -> Result { 73 | self.runtime_call("Core_version", vec![], at_block).await 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/authority_discovery.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::config::Config; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::{vec, vec::Vec}; 20 | use codec::Decode; 21 | 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait AuthorityDiscoveryApi: RuntimeApi { 24 | /// Retrieve authority identifiers of the current and next authority set. 25 | async fn authorities( 26 | &self, 27 | at_block: Option, 28 | ) -> Result>; 29 | } 30 | 31 | #[maybe_async::maybe_async(?Send)] 32 | impl AuthorityDiscoveryApi for RuntimeApiClient 33 | where 34 | T: Config, 35 | Client: Request, 36 | { 37 | async fn authorities( 38 | &self, 39 | at_block: Option, 40 | ) -> Result> { 41 | self.runtime_call("AuthorityDiscoveryApi_authorities", vec![], at_block).await 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/block_builder.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::{config::Config, UncheckedExtrinsic}; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::{vec, vec::Vec}; 20 | use sp_core::{Bytes, Encode}; 21 | use sp_inherents::{CheckInherentsResult, InherentData}; 22 | use sp_runtime::ApplyExtrinsicResult; 23 | 24 | #[maybe_async::maybe_async(?Send)] 25 | pub trait BlockBuilderApi: RuntimeApi { 26 | type ApplyExtrinsicResult; 27 | type Block; 28 | type InherentData; 29 | type CheckInherentsResult; 30 | type Header; 31 | 32 | /// Apply the given extrinsic. 33 | async fn apply_extrinsic( 34 | &self, 35 | extrinsic: UncheckedExtrinsic, 36 | at_block: Option, 37 | ) -> Result 38 | where 39 | Address: Encode, 40 | Call: Encode, 41 | Signature: Encode, 42 | TransactionExtension: Encode; 43 | 44 | /// Apply the given opaque extrinsic. 45 | async fn apply_opaque_extrinsic( 46 | &self, 47 | extrinsic: Vec, 48 | at_block: Option, 49 | ) -> Result; 50 | 51 | /// Check that the inherents are valid. 52 | async fn check_inherents( 53 | &self, 54 | block: Self::Block, 55 | data: Self::InherentData, 56 | at_block: Option, 57 | ) -> Result; 58 | 59 | /// Finish the current block. 60 | async fn finalize_block(&self, at_block: Option) -> Result; 61 | 62 | /// Generate inherent extrinsics and return them as encoded Bytes. 63 | async fn inherent_extrinsics( 64 | &self, 65 | inherent: Self::InherentData, 66 | at_block: Option, 67 | ) -> Result>; 68 | } 69 | 70 | #[maybe_async::maybe_async(?Send)] 71 | impl BlockBuilderApi for RuntimeApiClient 72 | where 73 | T: Config, 74 | Client: Request, 75 | { 76 | type ApplyExtrinsicResult = ApplyExtrinsicResult; 77 | type Block = T::Block; 78 | type InherentData = InherentData; 79 | type CheckInherentsResult = CheckInherentsResult; 80 | type Header = T::Header; 81 | 82 | async fn apply_extrinsic( 83 | &self, 84 | extrinsic: UncheckedExtrinsic, 85 | at_block: Option, 86 | ) -> Result 87 | where 88 | Address: Encode, 89 | Call: Encode, 90 | Signature: Encode, 91 | TransactionExtension: Encode, 92 | { 93 | self.apply_opaque_extrinsic(extrinsic.encode(), at_block).await 94 | } 95 | 96 | async fn apply_opaque_extrinsic( 97 | &self, 98 | extrinsic: Vec, 99 | at_block: Option, 100 | ) -> Result { 101 | self.runtime_call("BlockBuilder_apply_extrinsic", vec![extrinsic], at_block) 102 | .await 103 | } 104 | 105 | async fn check_inherents( 106 | &self, 107 | block: Self::Block, 108 | data: Self::InherentData, 109 | at_block: Option, 110 | ) -> Result { 111 | self.runtime_call( 112 | "BlockBuilder_check_inherents", 113 | vec![block.encode(), data.encode()], 114 | at_block, 115 | ) 116 | .await 117 | } 118 | 119 | async fn finalize_block(&self, at_block: Option) -> Result { 120 | self.runtime_call("BlockBuilder_finalize_block", vec![], at_block).await 121 | } 122 | 123 | async fn inherent_extrinsics( 124 | &self, 125 | inherent: Self::InherentData, 126 | at_block: Option, 127 | ) -> Result> { 128 | self.runtime_call("BlockBuilder_inherent_extrinsics", vec![inherent.encode()], at_block) 129 | .await 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/mmr.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::{config::Config, EncodableOpaqueLeaf, MmrError, Proof}; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::{vec, vec::Vec}; 20 | use core::result::Result as StdResult; 21 | use sp_core::Encode; 22 | 23 | #[maybe_async::maybe_async(?Send)] 24 | pub trait MmrApi: RuntimeApi { 25 | type Error; 26 | type BlockNumber; 27 | type EncodableOpaqueLeaf; 28 | type Proof; 29 | 30 | /// Generate MMR proof for the given block numbers. 31 | #[allow(clippy::type_complexity)] 32 | async fn generate_proof( 33 | &self, 34 | block_numbers: Vec, 35 | best_known_block_number: Option, 36 | at_block: Option, 37 | ) -> Result, Self::Proof), Self::Error>>; 38 | 39 | /// Return the on-chain MMR root hash. 40 | async fn root( 41 | &self, 42 | at_block: Option, 43 | ) -> Result, Self::Error>>; 44 | 45 | /// Verify MMR proof against on-chain MMR. 46 | async fn verify_proof( 47 | &self, 48 | leaves: Vec, 49 | proof: Self::Proof, 50 | at_block: Option, 51 | ) -> Result>; 52 | 53 | /// Verify MMR proof against given root hash. 54 | async fn verify_proof_stateless( 55 | &self, 56 | root: Self::Hash, 57 | leaves: Vec, 58 | proof: Self::Proof, 59 | at_block: Option, 60 | ) -> Result>; 61 | } 62 | 63 | #[maybe_async::maybe_async(?Send)] 64 | impl MmrApi for RuntimeApiClient 65 | where 66 | T: Config, 67 | Client: Request, 68 | { 69 | type Error = MmrError; 70 | type BlockNumber = T::BlockNumber; 71 | type EncodableOpaqueLeaf = EncodableOpaqueLeaf; 72 | type Proof = Proof; 73 | 74 | async fn generate_proof( 75 | &self, 76 | block_numbers: Vec, 77 | best_known_block_number: Option, 78 | at_block: Option, 79 | ) -> Result, Self::Proof), Self::Error>> { 80 | self.runtime_call( 81 | "MmrApi_generate_proof", 82 | vec![block_numbers.encode(), best_known_block_number.encode()], 83 | at_block, 84 | ) 85 | .await 86 | } 87 | 88 | async fn root( 89 | &self, 90 | at_block: Option, 91 | ) -> Result, Self::Error>> { 92 | self.runtime_call("MmrApi_root", vec![], at_block).await 93 | } 94 | 95 | async fn verify_proof( 96 | &self, 97 | leaves: Vec, 98 | proof: Self::Proof, 99 | at_block: Option, 100 | ) -> Result> { 101 | self.runtime_call("MmrApi_verify_proof", vec![leaves.encode(), proof.encode()], at_block) 102 | .await 103 | } 104 | 105 | async fn verify_proof_stateless( 106 | &self, 107 | root: Self::Hash, 108 | leaves: Vec, 109 | proof: Self::Proof, 110 | at_block: Option, 111 | ) -> Result> { 112 | self.runtime_call( 113 | "MmrApi_verify_proof_stateless", 114 | vec![root.encode(), leaves.encode(), proof.encode()], 115 | at_block, 116 | ) 117 | .await 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | pub use self::{ 15 | account_nonce::*, api_core::*, authority_discovery::*, block_builder::*, metadata::*, mmr::*, 16 | session_keys::*, staking::*, transaction_payment::*, transaction_payment_call::*, 17 | }; 18 | 19 | pub mod account_nonce; 20 | pub mod api_core; 21 | pub mod authority_discovery; 22 | pub mod block_builder; 23 | pub mod metadata; 24 | pub mod mmr; 25 | pub mod session_keys; 26 | pub mod staking; 27 | pub mod transaction_payment; 28 | pub mod transaction_payment_call; 29 | 30 | use crate::{api::Result, rpc::Request}; 31 | use ac_compose_macros::rpc_params; 32 | use ac_primitives::config::Config; 33 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 34 | use alloc::boxed::Box; 35 | use alloc::{sync::Arc, vec::Vec}; 36 | use codec::Decode; 37 | use core::marker::PhantomData; 38 | use sp_core::Bytes; 39 | 40 | #[derive(Clone)] 41 | pub struct RuntimeApiClient { 42 | client: Arc, 43 | _phantom: PhantomData, 44 | } 45 | 46 | impl RuntimeApiClient { 47 | pub fn new(client: Arc) -> Self { 48 | Self { client, _phantom: PhantomData } 49 | } 50 | } 51 | 52 | #[maybe_async::maybe_async(?Send)] 53 | pub trait RuntimeApi { 54 | type Hash; 55 | 56 | /// Query a runtime api call with automatic decoding to the expected return type. 57 | async fn runtime_call( 58 | &self, 59 | method: &str, 60 | data: Vec>, 61 | at_block: Option, 62 | ) -> Result; 63 | 64 | /// Query a raw runtime api call without decoding. 65 | async fn opaque_runtime_call( 66 | &self, 67 | method: &str, 68 | data: Vec>, 69 | at_block: Option, 70 | ) -> Result; 71 | 72 | // Perform a rpc call to a builtin on the chain. 73 | async fn rpc_call( 74 | &self, 75 | method: &str, 76 | data: Option, 77 | at_block: Option, 78 | ) -> Result; 79 | } 80 | 81 | #[maybe_async::maybe_async(?Send)] 82 | impl RuntimeApi for RuntimeApiClient 83 | where 84 | T: Config, 85 | Client: Request, 86 | { 87 | type Hash = T::Hash; 88 | 89 | async fn runtime_call( 90 | &self, 91 | method: &str, 92 | data: Vec>, 93 | at_block: Option, 94 | ) -> Result { 95 | let bytes = self.opaque_runtime_call(method, data, at_block).await?; 96 | Ok(Decode::decode(&mut bytes.0.as_slice())?) 97 | } 98 | 99 | async fn opaque_runtime_call( 100 | &self, 101 | method: &str, 102 | data: Vec>, 103 | at_block: Option, 104 | ) -> Result { 105 | let data = match data.is_empty() { 106 | true => None, 107 | false => { 108 | let mut appended_data = Vec::new(); 109 | for mut item in data { 110 | appended_data.append(&mut item); 111 | } 112 | Some(appended_data.into()) 113 | }, 114 | }; 115 | self.rpc_call(method, data, at_block).await 116 | } 117 | 118 | async fn rpc_call( 119 | &self, 120 | method: &str, 121 | data: Option, 122 | at_block: Option, 123 | ) -> Result { 124 | let extracted_data: Bytes = match data { 125 | Some(data) => data, 126 | None => Vec::new().into(), 127 | }; 128 | let return_bytes = self 129 | .client 130 | .request("state_call", rpc_params![method, extracted_data, at_block]) 131 | .await?; 132 | Ok(return_bytes) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/session_keys.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::config::Config; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::{vec, vec::Vec}; 20 | use codec::Encode; 21 | use sp_core::{crypto::KeyTypeId, Bytes}; 22 | 23 | #[maybe_async::maybe_async(?Send)] 24 | pub trait SessionKeysApi: RuntimeApi { 25 | type KeyTypeId; 26 | 27 | /// Decode the given public session keys. 28 | #[allow(clippy::type_complexity)] 29 | async fn decode_session_keys( 30 | &self, 31 | encoded: Bytes, 32 | at_block: Option, 33 | ) -> Result>>; 34 | 35 | /// Generate a set of session keys with optionally using the given seed. 36 | async fn generate_session_keys( 37 | &self, 38 | seed: Option, 39 | at_block: Option, 40 | ) -> Result; 41 | } 42 | 43 | #[maybe_async::maybe_async(?Send)] 44 | impl SessionKeysApi for RuntimeApiClient 45 | where 46 | T: Config, 47 | Client: Request, 48 | { 49 | type KeyTypeId = KeyTypeId; 50 | 51 | async fn decode_session_keys( 52 | &self, 53 | encoded: Bytes, 54 | at_block: Option, 55 | ) -> Result>> { 56 | self.runtime_call("SessionKeys_decode_session_keys", vec![encoded.0], at_block) 57 | .await 58 | } 59 | 60 | async fn generate_session_keys( 61 | &self, 62 | seed: Option, 63 | at_block: Option, 64 | ) -> Result { 65 | self.runtime_call("SessionKeys_generate_session_keys", vec![seed.encode()], at_block) 66 | .await 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/staking.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::config::Config; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::vec; 20 | use sp_core::Encode; 21 | 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait StakingApi: RuntimeApi { 24 | type Balance; 25 | 26 | /// Returns the nominations quota for a nominator with a given balance. 27 | async fn nominations_quota( 28 | &self, 29 | balance: Self::Balance, 30 | at_block: Option, 31 | ) -> Result; 32 | } 33 | 34 | #[maybe_async::maybe_async(?Send)] 35 | impl StakingApi for RuntimeApiClient 36 | where 37 | T: Config, 38 | Client: Request, 39 | { 40 | type Balance = T::Balance; 41 | 42 | async fn nominations_quota( 43 | &self, 44 | balance: Self::Balance, 45 | at_block: Option, 46 | ) -> Result { 47 | self.runtime_call("StakingApi_nominations_quota", vec![balance.encode()], at_block) 48 | .await 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api-client/src/api/runtime_api/transaction_payment_call.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use super::{RuntimeApi, RuntimeApiClient}; 15 | use crate::{api::Result, rpc::Request}; 16 | use ac_primitives::{config::Config, FeeDetails, RuntimeDispatchInfo, Weight}; 17 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 18 | use alloc::boxed::Box; 19 | use alloc::vec; 20 | use sp_core::Encode; 21 | 22 | #[maybe_async::maybe_async(?Send)] 23 | pub trait TransactionPaymentCallApi: RuntimeApi { 24 | type FeeDetails; 25 | type RuntimeDispatchInfo; 26 | type Balance; 27 | type Weight; 28 | 29 | /// Query the call fee details. 30 | async fn query_call_fee_details( 31 | &self, 32 | call: Call, 33 | length: u32, 34 | at_block: Option, 35 | ) -> Result; 36 | 37 | /// Query the call info 38 | async fn query_call_info( 39 | &self, 40 | call: Call, 41 | length: u32, 42 | at_block: Option, 43 | ) -> Result; 44 | 45 | /// Query the output of the current LengthToFee given some input. 46 | async fn query_length_to_fee_call( 47 | &self, 48 | length: u32, 49 | at_block: Option, 50 | ) -> Result; 51 | 52 | /// Query the output of the current WeightToFee given some input. 53 | async fn query_weight_to_fee_call( 54 | &self, 55 | weight: Self::Weight, 56 | at_block: Option, 57 | ) -> Result; 58 | } 59 | 60 | #[maybe_async::maybe_async(?Send)] 61 | impl TransactionPaymentCallApi for RuntimeApiClient 62 | where 63 | T: Config, 64 | Client: Request, 65 | { 66 | type FeeDetails = FeeDetails; 67 | type RuntimeDispatchInfo = RuntimeDispatchInfo; 68 | type Balance = T::Balance; 69 | type Weight = Weight; 70 | 71 | async fn query_call_fee_details( 72 | &self, 73 | call: Call, 74 | length: u32, 75 | at_block: Option, 76 | ) -> Result { 77 | self.runtime_call( 78 | "TransactionPaymentCallApi_query_call_fee_details", 79 | vec![call.encode(), length.encode()], 80 | at_block, 81 | ) 82 | .await 83 | } 84 | 85 | async fn query_call_info( 86 | &self, 87 | call: Call, 88 | length: u32, 89 | at_block: Option, 90 | ) -> Result { 91 | self.runtime_call( 92 | "TransactionPaymentCallApi_query_call_info", 93 | vec![call.encode(), length.encode()], 94 | at_block, 95 | ) 96 | .await 97 | } 98 | 99 | async fn query_length_to_fee_call( 100 | &self, 101 | length: u32, 102 | at_block: Option, 103 | ) -> Result { 104 | self.runtime_call( 105 | "TransactionPaymentCallApi_query_length_to_fee", 106 | vec![length.encode()], 107 | at_block, 108 | ) 109 | .await 110 | } 111 | 112 | async fn query_weight_to_fee_call( 113 | &self, 114 | weight: Self::Weight, 115 | at_block: Option, 116 | ) -> Result { 117 | self.runtime_call( 118 | "TransactionPaymentCallApi_query_weight_to_fee", 119 | vec![weight.encode()], 120 | at_block, 121 | ) 122 | .await 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /api-client/src/extrinsic/balances.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Extrinsics for `pallet-balances`. 19 | //! https://polkadot.js.org/docs/substrate/extrinsics/#balances 20 | 21 | use crate::{api::Api, rpc::Request}; 22 | use ac_compose_macros::compose_extrinsic; 23 | use ac_primitives::{ 24 | config::Config, extrinsic_params::ExtrinsicParams, extrinsics::CallIndex, SignExtrinsic, 25 | UncheckedExtrinsic, 26 | }; 27 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 28 | use alloc::boxed::Box; 29 | use codec::{Compact, Encode}; 30 | 31 | pub const BALANCES_MODULE: &str = "Balances"; 32 | pub const TRANSFER_ALLOW_DEATH: &str = "transfer_allow_death"; 33 | pub const TRANSFER_KEEP_ALIVE: &str = "transfer_keep_alive"; 34 | pub const FORCE_SET_BALANCE: &str = "force_set_balance"; 35 | 36 | /// Call for a balance transfer. 37 | pub type TransferAllowDeathCall = (CallIndex, Address, Compact); 38 | 39 | /// Call for a balance transfer. 40 | pub type TransferKeepAliveCall = (CallIndex, Address, Compact); 41 | 42 | /// Call to the balance of an account. 43 | pub type ForceSetBalanceCall = (CallIndex, Address, Compact); 44 | 45 | #[maybe_async::maybe_async(?Send)] 46 | pub trait BalancesExtrinsics { 47 | type Balance; 48 | type Address; 49 | type Extrinsic; 50 | 51 | /// Transfer some liquid free balance to another account. 52 | #[allow(clippy::type_complexity)] 53 | async fn balance_transfer_allow_death( 54 | &self, 55 | to: Self::Address, 56 | amount: Self::Balance, 57 | ) -> Option>>; 58 | 59 | /// Transfer some liquid free balance to another account. 60 | #[allow(clippy::type_complexity)] 61 | async fn balance_transfer_keep_alive( 62 | &self, 63 | to: Self::Address, 64 | amount: Self::Balance, 65 | ) -> Option>>; 66 | 67 | /// Set the balances of a given account. 68 | #[allow(clippy::type_complexity)] 69 | async fn balance_force_set_balance( 70 | &self, 71 | who: Self::Address, 72 | free_balance: Self::Balance, 73 | ) -> Option>>; 74 | } 75 | 76 | #[maybe_async::maybe_async(?Send)] 77 | impl BalancesExtrinsics for Api 78 | where 79 | T: Config, 80 | Client: Request, 81 | Compact: Encode, 82 | { 83 | type Balance = T::Balance; 84 | type Address = >::ExtrinsicAddress; 85 | type Extrinsic = UncheckedExtrinsic< 86 | Self::Address, 87 | Call, 88 | >::Signature, 89 | >::TxExtension, 90 | >; 91 | 92 | async fn balance_transfer_allow_death( 93 | &self, 94 | to: Self::Address, 95 | amount: Self::Balance, 96 | ) -> Option>> { 97 | compose_extrinsic!(self, BALANCES_MODULE, TRANSFER_ALLOW_DEATH, to, Compact(amount)) 98 | } 99 | 100 | #[allow(clippy::type_complexity)] 101 | async fn balance_transfer_keep_alive( 102 | &self, 103 | to: Self::Address, 104 | amount: Self::Balance, 105 | ) -> Option>> { 106 | compose_extrinsic!(self, BALANCES_MODULE, TRANSFER_KEEP_ALIVE, to, Compact(amount)) 107 | } 108 | 109 | async fn balance_force_set_balance( 110 | &self, 111 | who: Self::Address, 112 | free_balance: Self::Balance, 113 | ) -> Option>> { 114 | compose_extrinsic!(self, BALANCES_MODULE, FORCE_SET_BALANCE, who, Compact(free_balance)) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /api-client/src/extrinsic/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Offers some predefined extrinsics for common runtime modules. 19 | 20 | pub use balances::BalancesExtrinsics; 21 | #[cfg(feature = "contracts-xt")] 22 | pub use contracts::ContractsExtrinsics; 23 | #[cfg(feature = "staking-xt")] 24 | pub use staking::StakingExtrinsics; 25 | pub use utility::UtilityExtrinsics; 26 | 27 | pub mod balances; 28 | #[cfg(feature = "contracts-xt")] 29 | pub mod contracts; 30 | pub mod offline_extrinsic; 31 | #[cfg(feature = "staking-xt")] 32 | pub mod staking; 33 | pub mod utility; 34 | -------------------------------------------------------------------------------- /api-client/src/extrinsic/offline_extrinsic.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Helper function to easily create extrinsics offline (without getter calls to the node). 19 | 20 | use crate::Api; 21 | use ac_compose_macros::compose_extrinsic_offline; 22 | use ac_primitives::{ 23 | config::Config, extrinsic_params::ExtrinsicParams, Preamble, SignExtrinsic, UncheckedExtrinsic, 24 | }; 25 | use codec::Encode; 26 | 27 | type ExtrinsicAddress = 28 | <::ExtrinsicSigner as SignExtrinsic<::AccountId>>::ExtrinsicAddress; 29 | type Signature = 30 | <::ExtrinsicSigner as SignExtrinsic<::AccountId>>::Signature; 31 | type TxExtension = <::ExtrinsicParams as ExtrinsicParams< 32 | ::Index, 33 | ::Hash, 34 | >>::TxExtension; 35 | 36 | impl Api { 37 | /// Wrapper around the `compose_extrinsic_offline!` macro to be less verbose. 38 | pub fn compose_extrinsic_offline( 39 | &self, 40 | call: Call, 41 | nonce: T::Index, 42 | ) -> UncheckedExtrinsic, Call, Signature, TxExtension> { 43 | match self.signer() { 44 | Some(signer) => compose_extrinsic_offline!(signer, call, self.extrinsic_params(nonce)), 45 | None => UncheckedExtrinsic { preamble: Preamble::Bare(5), function: call }, 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /api-client/src/extrinsic/utility.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Extrinsics for `pallet-utility`. 19 | //! https://polkadot.js.org/docs/substrate/extrinsics/#utility 20 | 21 | use crate::{rpc::Request, Api}; 22 | use ac_compose_macros::compose_extrinsic; 23 | use ac_primitives::{ 24 | config::Config, extrinsic_params::ExtrinsicParams, extrinsics::CallIndex, SignExtrinsic, 25 | UncheckedExtrinsic, 26 | }; 27 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 28 | use alloc::boxed::Box; 29 | use alloc::vec::Vec; 30 | use codec::{Decode, Encode}; 31 | 32 | const UTILITY_MODULE: &str = "Utility"; 33 | const BATCH: &str = "batch"; 34 | const FORCE_BATCH: &str = "force_batch"; 35 | 36 | #[derive(Clone, Eq, PartialEq, Encode, Decode, Debug)] 37 | pub struct Batch { 38 | pub calls: Vec, 39 | } 40 | 41 | pub type BatchCall = (CallIndex, Batch); 42 | 43 | #[maybe_async::maybe_async(?Send)] 44 | pub trait UtilityExtrinsics { 45 | type Extrinsic; 46 | 47 | // Send a batch of dispatch calls. 48 | async fn batch( 49 | &self, 50 | calls: Vec, 51 | ) -> Option>>; 52 | 53 | // Send a batch of dispatch calls. Unlike batch, it allows errors and won't interrupt. 54 | async fn force_batch( 55 | &self, 56 | calls: Vec, 57 | ) -> Option>>; 58 | } 59 | 60 | #[maybe_async::maybe_async(?Send)] 61 | impl UtilityExtrinsics for Api 62 | where 63 | T: Config, 64 | Client: Request, 65 | { 66 | type Extrinsic = UncheckedExtrinsic< 67 | >::ExtrinsicAddress, 68 | Call, 69 | >::Signature, 70 | >::TxExtension, 71 | >; 72 | 73 | async fn batch( 74 | &self, 75 | calls: Vec, 76 | ) -> Option>> { 77 | let calls = Batch { calls }; 78 | compose_extrinsic!(self, UTILITY_MODULE, BATCH, calls) 79 | } 80 | 81 | async fn force_batch( 82 | &self, 83 | calls: Vec, 84 | ) -> Option>> { 85 | let calls = Batch { calls }; 86 | compose_extrinsic!(self, UTILITY_MODULE, FORCE_BATCH, calls) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /api-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | #![cfg_attr(not(feature = "std"), no_std)] 18 | 19 | extern crate alloc; 20 | 21 | use sp_crypto_hashing::twox_128; 22 | use sp_storage::StorageKey; 23 | 24 | pub use ac_compose_macros; 25 | pub use ac_node_api; 26 | pub use ac_primitives; 27 | pub use api::*; // Re-export everything 28 | 29 | pub mod api; 30 | pub mod extrinsic; 31 | pub mod rpc; 32 | 33 | /// Returns the concatenated 128 bit hash of the given module and specific storage key 34 | /// as a full Substrate StorageKey. 35 | pub fn storage_key(module: &str, storage_key_name: &str) -> StorageKey { 36 | let mut key = twox_128(module.as_bytes()).to_vec(); 37 | key.extend(twox_128(storage_key_name.as_bytes())); 38 | StorageKey(key) 39 | } 40 | -------------------------------------------------------------------------------- /api-client/src/rpc/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use alloc::{boxed::Box, string::String}; 19 | use core::error::Error as ErrorT; 20 | 21 | pub type Result = core::result::Result; 22 | 23 | #[derive(Debug)] 24 | pub enum Error { 25 | SerdeJson(serde_json::error::Error), 26 | ExtrinsicFailed(String), 27 | MpscSend(String), 28 | InvalidUrl(String), 29 | RecvError(String), 30 | Io(String), 31 | MaxConnectionAttemptsExceeded, 32 | ConnectionClosed, 33 | Client(Box), 34 | } 35 | 36 | impl From for Error { 37 | fn from(error: serde_json::error::Error) -> Self { 38 | Self::SerdeJson(error) 39 | } 40 | } 41 | 42 | #[cfg(feature = "ws-client")] 43 | impl From for Error { 44 | fn from(error: ws::Error) -> Self { 45 | Self::Client(Box::new(error)) 46 | } 47 | } 48 | 49 | #[cfg(feature = "tungstenite-client")] 50 | impl From for Error { 51 | fn from(error: tungstenite::Error) -> Self { 52 | Self::Client(Box::new(error)) 53 | } 54 | } 55 | 56 | #[cfg(feature = "std")] 57 | #[allow(unused_imports)] 58 | pub use std_only::*; 59 | #[cfg(feature = "std")] 60 | mod std_only { 61 | use super::*; 62 | use std::sync::mpsc::{RecvError, SendError}; 63 | 64 | impl From> for Error { 65 | fn from(error: SendError) -> Self { 66 | Self::MpscSend(error.0) 67 | } 68 | } 69 | 70 | impl From for Error { 71 | fn from(error: RecvError) -> Self { 72 | Self::RecvError(format!("{error:?}")) 73 | } 74 | } 75 | 76 | impl From for Error { 77 | fn from(error: std::io::Error) -> Self { 78 | Self::Io(format!("{error:?}")) 79 | } 80 | } 81 | 82 | impl From for Error { 83 | fn from(error: url::ParseError) -> Self { 84 | Self::InvalidUrl(format!("{error:?}")) 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /api-client/src/rpc/helpers.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use alloc::{ 19 | format, 20 | string::{String, ToString}, 21 | }; 22 | use serde_json::Value; 23 | 24 | pub fn read_subscription_id(value: &Value) -> Option { 25 | value["result"].as_str().map(|str| str.to_string()) 26 | } 27 | 28 | pub fn read_error_message(value: &Value, msg: &str) -> String { 29 | match value["error"].as_str() { 30 | Some(error_message) => error_message.to_string(), 31 | None => format!("Unexpected Response: {msg}"), 32 | } 33 | } 34 | 35 | pub fn subscription_id_matches(value: &Value, subscription_id: &str) -> bool { 36 | match value["params"]["subscription"].as_str() { 37 | Some(retrieved_subscription_id) => subscription_id == retrieved_subscription_id, 38 | None => false, 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | use serde_json::json; 46 | 47 | #[test] 48 | fn read_valid_subscription_response() { 49 | let subcription_id = "tejkataa12124a"; 50 | let value = json!({ 51 | "result": subcription_id, 52 | "id": 43, 53 | "and_so_on": "test", 54 | }); 55 | 56 | let maybe_subcription_id = read_subscription_id(&value); 57 | assert_eq!(maybe_subcription_id, Some(subcription_id.to_string())); 58 | } 59 | 60 | #[test] 61 | fn read_invalid_subscription_response() { 62 | let subcription_id = "tejkataa12124a"; 63 | let value = json!({ 64 | "error": subcription_id, 65 | "id": 43, 66 | "and_so_on": "test", 67 | }); 68 | 69 | let maybe_subcription_id = read_subscription_id(&value); 70 | assert!(maybe_subcription_id.is_none()); 71 | } 72 | 73 | #[test] 74 | fn read_error_message_returns_error_if_available() { 75 | let error_message = "some_error_message"; 76 | let value = json!({ 77 | "error": error_message, 78 | "id": 43, 79 | "and_so_on": "test", 80 | }); 81 | 82 | let msg = serde_json::to_string(&value).unwrap(); 83 | 84 | let message = read_error_message(&value, &msg); 85 | assert!(message.contains(error_message)); 86 | assert!(message.contains("error")); 87 | } 88 | 89 | #[test] 90 | fn read_error_message_returns_full_msg_if_error_is_not_available() { 91 | let error_message = "some_error_message"; 92 | let value = json!({ 93 | "result": error_message, 94 | "id": 43, 95 | "and_so_on": "test", 96 | }); 97 | 98 | let msg = serde_json::to_string(&value).unwrap(); 99 | 100 | let message = read_error_message(&value, &msg); 101 | assert!(message.contains(&msg)); 102 | } 103 | 104 | #[test] 105 | fn subscription_id_matches_returns_true_for_equal_id() { 106 | let subcription_id = "tejkataa12124a"; 107 | let value = json!({ 108 | "params": { 109 | "subscription": subcription_id, 110 | "message": "Test" 111 | }, 112 | "id": 43, 113 | "and_so_on": "test", 114 | }); 115 | 116 | assert!(subscription_id_matches(&value, subcription_id)); 117 | } 118 | 119 | #[test] 120 | fn subscription_id_matches_returns_false_for_not_equal_id() { 121 | let subcription_id = "tejkataa12124a"; 122 | let value = json!({ 123 | "params": { 124 | "subscription": "something else", 125 | "message": "Test" 126 | }, 127 | "id": 43, 128 | "and_so_on": "test", 129 | }); 130 | 131 | assert!(!subscription_id_matches(&value, subcription_id)); 132 | } 133 | 134 | #[test] 135 | fn subscription_id_matches_returns_false_for_missing_subscription() { 136 | let subcription_id = "tejkataa12124a"; 137 | let value = json!({ 138 | "params": { 139 | "result": subcription_id, 140 | "message": "Test" 141 | }, 142 | "id": 43, 143 | "and_so_on": "test", 144 | }); 145 | 146 | assert!(!subscription_id_matches(&value, subcription_id)); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /api-client/src/rpc/jsonrpsee_client/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use crate::rpc::{Error, Request, Result, RpcParams, Subscribe}; 15 | use jsonrpsee::{ 16 | client_transport::ws::{Url, WsTransportClientBuilder}, 17 | core::{ 18 | client::{Client, ClientBuilder, ClientT, Error as JsonrpseeError, SubscriptionClientT}, 19 | traits::ToRpcParams, 20 | }, 21 | }; 22 | use serde::de::DeserializeOwned; 23 | use serde_json::value::RawValue; 24 | use std::sync::Arc; 25 | 26 | pub use subscription::SubscriptionWrapper; 27 | 28 | mod subscription; 29 | 30 | #[derive(Clone)] 31 | pub struct JsonrpseeClient { 32 | inner: Arc, 33 | } 34 | 35 | impl JsonrpseeClient { 36 | /// Create a new client to a local Substrate node with default port. 37 | pub async fn with_default_url() -> Result { 38 | Self::new("ws://127.0.0.1:9944").await 39 | } 40 | 41 | /// Create a new client with the given url string. 42 | /// Example url input: "ws://127.0.0.1:9944" 43 | pub async fn new(url: &str) -> Result { 44 | let parsed_url: Url = url.parse().map_err(|e| Error::Client(Box::new(e)))?; 45 | let (tx, rx) = WsTransportClientBuilder::default() 46 | .build(parsed_url) 47 | .await 48 | .map_err(|e| Error::Client(Box::new(e)))?; 49 | let client = ClientBuilder::default() 50 | .max_buffer_capacity_per_subscription(4096) 51 | .build_with_tokio(tx, rx); 52 | Ok(Self { inner: Arc::new(client) }) 53 | } 54 | 55 | /// Create a new client with the given address, port and max number of reconnection attempts. 56 | /// Example input: 57 | /// - address: "ws://127.0.0.1" 58 | /// - port: 9944 59 | pub async fn new_with_port(address: &str, port: u32) -> Result { 60 | let url = format!("{address}:{port:?}"); 61 | Self::new(&url).await 62 | } 63 | 64 | /// Create a new client with a user-generated Jsonrpsee Client. 65 | pub fn new_with_client(client: Client) -> Self { 66 | let inner = Arc::new(client); 67 | Self { inner } 68 | } 69 | } 70 | 71 | impl JsonrpseeClient { 72 | /// Checks if the client is connected to the target. 73 | pub fn is_connected(&self) -> bool { 74 | self.inner.is_connected() 75 | } 76 | 77 | /// This is similar to [`Client::on_disconnect`] but it can be used to get 78 | /// the reason why the client was disconnected but it's not cancel-safe. 79 | /// 80 | /// The typical use-case is that this method will be called after 81 | /// [`Client::on_disconnect`] has returned in a "select loop". 82 | /// 83 | /// # Cancel-safety 84 | /// 85 | /// This method is not cancel-safe 86 | pub async fn disconnect_reason(&self) -> JsonrpseeError { 87 | self.inner.disconnect_reason().await 88 | } 89 | 90 | /// Completes when the client is disconnected or the client's background task encountered an error. 91 | /// If the client is already disconnected, the future produced by this method will complete immediately. 92 | /// 93 | /// # Cancel safety 94 | /// 95 | /// This method is cancel safe. 96 | pub async fn on_disconnect(&self) { 97 | self.inner.on_disconnect().await; 98 | } 99 | } 100 | 101 | #[maybe_async::async_impl(?Send)] 102 | impl Request for JsonrpseeClient { 103 | async fn request(&self, method: &str, params: RpcParams) -> Result { 104 | self.inner 105 | .request(method, RpcParamsWrapper(params)) 106 | .await 107 | .map_err(|e| Error::Client(Box::new(e))) 108 | } 109 | } 110 | 111 | #[maybe_async::async_impl(?Send)] 112 | impl Subscribe for JsonrpseeClient { 113 | type Subscription 114 | = SubscriptionWrapper 115 | where 116 | Notification: DeserializeOwned; 117 | 118 | async fn subscribe( 119 | &self, 120 | sub: &str, 121 | params: RpcParams, 122 | unsub: &str, 123 | ) -> Result> { 124 | self.inner 125 | .subscribe(sub, RpcParamsWrapper(params), unsub) 126 | .await 127 | .map(|sub| sub.into()) 128 | .map_err(|e| Error::Client(Box::new(e))) 129 | } 130 | } 131 | 132 | struct RpcParamsWrapper(RpcParams); 133 | 134 | impl ToRpcParams for RpcParamsWrapper { 135 | fn to_rpc_params(self) -> core::result::Result>, serde_json::Error> { 136 | if let Some(json) = self.0.build() { 137 | RawValue::from_string(json).map(Some) 138 | } else { 139 | Ok(None) 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /api-client/src/rpc/jsonrpsee_client/subscription.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use crate::rpc::{Error, HandleSubscription, Result}; 15 | use jsonrpsee::core::client::Subscription; 16 | use serde::de::DeserializeOwned; 17 | 18 | #[derive(Debug)] 19 | pub struct SubscriptionWrapper { 20 | inner: Subscription, 21 | } 22 | 23 | #[maybe_async::async_impl(?Send)] 24 | impl HandleSubscription 25 | for SubscriptionWrapper 26 | { 27 | async fn next(&mut self) -> Option> { 28 | self.inner 29 | .next() 30 | .await 31 | .map(|result| result.map_err(|e| Error::Client(Box::new(e)))) 32 | } 33 | 34 | async fn unsubscribe(self) -> Result<()> { 35 | self.inner.unsubscribe().await.map_err(|e| Error::Client(Box::new(e))) 36 | } 37 | } 38 | 39 | impl From> for SubscriptionWrapper { 40 | fn from(inner: Subscription) -> Self { 41 | Self { inner } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /api-client/src/rpc/mocks.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use crate::rpc::{Request, Result}; 15 | use ac_primitives::RpcParams; 16 | use serde::de::DeserializeOwned; 17 | use std::{collections::HashMap, sync::RwLock}; 18 | 19 | type MethodKey = String; 20 | type SerializedValue = String; 21 | 22 | #[derive(Debug)] 23 | pub struct RpcClientMock { 24 | pub state: RwLock>, 25 | } 26 | 27 | impl RpcClientMock { 28 | pub fn new(state: HashMap) -> Self { 29 | Self { state: RwLock::new(state) } 30 | } 31 | 32 | pub fn update_entry(&self, key: MethodKey, value: SerializedValue) { 33 | let mut lock = self.state.write().unwrap(); 34 | lock.insert(key, value); 35 | } 36 | } 37 | 38 | #[maybe_async::maybe_async(?Send)] 39 | impl Request for RpcClientMock { 40 | async fn request(&self, method: &str, _params: RpcParams) -> Result { 41 | let lock = self.state.read().unwrap(); 42 | let response = lock.get(method).unwrap(); 43 | let deserialized_value: R = serde_json::from_str(response).unwrap(); 44 | Ok(deserialized_value) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api-client/src/rpc/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use ac_primitives::RpcParams; 19 | #[cfg(all(not(feature = "sync-api"), not(feature = "std")))] 20 | use alloc::boxed::Box; 21 | use alloc::string::{String, ToString}; 22 | use serde::de::DeserializeOwned; 23 | 24 | #[cfg(feature = "ws-client")] 25 | #[allow(deprecated)] 26 | pub use ws_client::WsRpcClient; 27 | #[cfg(feature = "ws-client")] 28 | #[allow(deprecated)] 29 | pub mod ws_client; 30 | 31 | #[cfg(feature = "tungstenite-client")] 32 | pub use tungstenite_client::TungsteniteRpcClient; 33 | #[cfg(feature = "tungstenite-client")] 34 | pub mod tungstenite_client; 35 | 36 | #[cfg(all(feature = "jsonrpsee-client", not(feature = "sync-api")))] 37 | pub use jsonrpsee_client::JsonrpseeClient; 38 | #[cfg(all(feature = "jsonrpsee-client", not(feature = "sync-api")))] 39 | #[allow(dead_code)] 40 | #[allow(unused_imports)] 41 | pub mod jsonrpsee_client; 42 | 43 | pub mod error; 44 | #[cfg(any(feature = "ws-client", feature = "tungstenite-client"))] 45 | mod helpers; 46 | 47 | pub use error::{Error, Result}; 48 | 49 | #[cfg(test)] 50 | pub mod mocks; 51 | 52 | /// Trait to be implemented by the ws-client for sending rpc requests and extrinsic. 53 | #[maybe_async::maybe_async(?Send)] 54 | pub trait Request { 55 | /// Sends a RPC request to the substrate node and returns the answer as string. 56 | async fn request(&self, method: &str, params: RpcParams) -> Result; 57 | } 58 | 59 | /// Trait to be implemented by the ws-client for subscribing to the substrate node. 60 | #[maybe_async::maybe_async(?Send)] 61 | pub trait Subscribe { 62 | type Subscription: HandleSubscription 63 | where 64 | Notification: DeserializeOwned; 65 | 66 | async fn subscribe( 67 | &self, 68 | sub: &str, 69 | params: RpcParams, 70 | unsub: &str, 71 | ) -> Result>; 72 | } 73 | 74 | /// Trait to use the full functionality of jsonrpseee Subscription type 75 | /// without actually enforcing it. 76 | #[maybe_async::maybe_async(?Send)] 77 | pub trait HandleSubscription { 78 | /// Returns the next notification from the stream. 79 | /// This may return `None` if the subscription has been terminated, 80 | /// which may happen if the channel becomes full or is dropped. 81 | /// 82 | /// **Note:** This has an identical signature to the [`StreamExt::next`] 83 | /// method (and delegates to that). Import [`StreamExt`] if you'd like 84 | /// access to other stream combinator methods. 85 | async fn next(&mut self) -> Option>; 86 | 87 | /// Unsubscribe and consume the subscription. 88 | async fn unsubscribe(self) -> Result<()>; 89 | } 90 | 91 | pub fn to_json_req(method: &str, params: RpcParams) -> Result { 92 | Ok(serde_json::json!({ 93 | "method": method, 94 | "params": params.to_json_value()?, 95 | "jsonrpc": "2.0", 96 | "id": "1", 97 | }) 98 | .to_string()) 99 | } 100 | -------------------------------------------------------------------------------- /api-client/src/rpc/tungstenite_client/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | pub use client::TungsteniteRpcClient; 19 | pub use subscription::TungsteniteSubscriptionWrapper; 20 | 21 | pub mod client; 22 | pub mod subscription; 23 | -------------------------------------------------------------------------------- /api-client/src/rpc/tungstenite_client/subscription.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use crate::rpc::{Error, HandleSubscription, Result}; 19 | use core::marker::PhantomData; 20 | use serde::de::DeserializeOwned; 21 | use std::sync::mpsc::Receiver; 22 | 23 | #[derive(Debug)] 24 | pub struct TungsteniteSubscriptionWrapper { 25 | receiver: Receiver, 26 | _phantom: PhantomData, 27 | } 28 | 29 | impl TungsteniteSubscriptionWrapper { 30 | pub fn new(receiver: Receiver) -> Self { 31 | Self { receiver, _phantom: Default::default() } 32 | } 33 | } 34 | 35 | #[maybe_async::maybe_async(?Send)] 36 | impl HandleSubscription 37 | for TungsteniteSubscriptionWrapper 38 | { 39 | async fn next(&mut self) -> Option> { 40 | let notification = match self.receiver.recv() { 41 | Ok(notif) => notif, 42 | // Sender was disconnected, therefore no further messages are to be expected. 43 | Err(_e) => return None, 44 | }; 45 | Some(serde_json::from_str(¬ification).map_err(|_| Error::ExtrinsicFailed(notification))) 46 | } 47 | 48 | async fn unsubscribe(self) -> Result<()> { 49 | // TODO: Nicer unsubscription. 50 | // We close ungracefully: Simply drop the receiver. This will turn 51 | // into an error on the sender side, terminating the websocket polling 52 | // loop. 53 | Ok(()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /api-client/src/rpc/ws_client/subscription.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use crate::rpc::{Error, HandleSubscription, Result}; 19 | use core::marker::PhantomData; 20 | use serde::de::DeserializeOwned; 21 | use std::sync::mpsc::Receiver; 22 | use ws::Sender as WsSender; 23 | 24 | #[derive(Debug)] 25 | pub struct WsSubscriptionWrapper { 26 | ws_sender: WsSender, 27 | receiver: Receiver, 28 | _phantom: PhantomData, 29 | } 30 | 31 | impl WsSubscriptionWrapper { 32 | pub fn new(ws_sender: WsSender, receiver: Receiver) -> Self { 33 | Self { ws_sender, receiver, _phantom: Default::default() } 34 | } 35 | } 36 | 37 | #[maybe_async::maybe_async(?Send)] 38 | impl HandleSubscription 39 | for WsSubscriptionWrapper 40 | { 41 | async fn next(&mut self) -> Option> { 42 | let notification = match self.receiver.recv() { 43 | Ok(notif) => notif, 44 | // Sender was disconnected, therefore no further messages are to be expected. 45 | Err(_) => return None, 46 | }; 47 | Some(serde_json::from_str(¬ification).map_err(|_| Error::ExtrinsicFailed(notification))) 48 | } 49 | 50 | async fn unsubscribe(self) -> Result<()> { 51 | self.ws_sender.clone().shutdown()?; 52 | Ok(()) 53 | } 54 | } 55 | 56 | impl Drop for WsSubscriptionWrapper { 57 | fn drop(&mut self) { 58 | if let Err(e) = self.ws_sender.shutdown() { 59 | log::error!("Could not properly shutdown websocket connection due to {e:?}"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /compose-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-compose-macros" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/scs/substrate-api-client" 8 | description = "Macros for creating Substrate extrinsics and rpc calls" 9 | readme = "README.md" 10 | categories = ["no-std"] 11 | 12 | 13 | [dependencies] 14 | log = { workspace = true } 15 | maybe-async = { workspace = true } 16 | 17 | # local 18 | ac-primitives = { workspace = true } 19 | 20 | [dev-dependencies] 21 | ac-node-api = { workspace = true } 22 | frame-metadata = { workspace = true } 23 | codec = { workspace = true } 24 | 25 | 26 | [features] 27 | default = ["std", "sync-api"] 28 | sync-api = ["maybe-async/is_sync"] 29 | # To support `no_std` builds in non-32 bit environments. 30 | disable_target_static_assertions = [ 31 | "ac-primitives/disable_target_static_assertions", 32 | ] 33 | std = [ 34 | "log/std", 35 | # local 36 | "ac-primitives/std", 37 | ] 38 | -------------------------------------------------------------------------------- /compose-macros/README.md: -------------------------------------------------------------------------------- 1 | # ac_compose_macros 2 | 3 | This crate is a submodule of the [substrate-api-client](https://github.com/scs/substrate-api-client). It contains macro definition for creating Substrate extrinsic and rpc calls for the api-client. 4 | -------------------------------------------------------------------------------- /compose-macros/src/rpc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 Parity Technologies (UK) Ltd. 2 | // 3 | // Permission is hereby granted, free of charge, to any 4 | // person obtaining a copy of this software and associated 5 | // documentation files (the "Software"), to deal in the 6 | // Software without restriction, including without 7 | // limitation the rights to use, copy, modify, merge, 8 | // publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software 10 | // is furnished to do so, subject to the following 11 | // conditions: 12 | // 13 | // The above copyright notice and this permission notice 14 | // shall be included in all copies or substantial portions 15 | // of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | // SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | // DEALINGS IN THE SOFTWARE. 26 | 27 | //! RPC parameters, originally belonging to jsonrpsee: 28 | //! https://github.com/paritytech/jsonrpsee 29 | //! It is copied & pasted here to avoid std dependencies. 30 | 31 | /// Convert the given values to a [`RpcParams`] as expected by a 32 | /// Rpc Client (http or websocket). 33 | /// 34 | /// # Panics 35 | /// 36 | /// Panics if the serialization of parameters fails. 37 | #[macro_export] 38 | macro_rules! rpc_params { 39 | ($($param:expr),*) => { 40 | { 41 | use $crate::primitives::RpcParams; 42 | 43 | let mut params = RpcParams::new(); 44 | $( 45 | if let Err(err) = params.insert($param) { 46 | panic!("Parameter `{}` cannot be serialized: {:?}", stringify!($param), err); 47 | } 48 | )* 49 | params 50 | } 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Developer Section 2 | 3 | ## TODO List for a new Release 4 | If a new release is released, the following tasks need to be done: 5 | 1. Update the .toml versions in accordance with the [Semantic Versioning](../README.md#version-numbers) section. 6 | 1. Create a new branch and change all github deps to crates.io deps. See https://github.com/scs/substrate-api-client/issues/812 for an example update. The [psvm](https://crates.io/crates/psvm) tool can be useful for updating the polkadot dependencies. 7 | 1. Add a new tag to the desired commit, see the [Tag generation](#tag-generation) section. 8 | 1. Update and release the new release (see [Automatic Release Generation](#automatic-release-generation)). 9 | 1. Publish to crates.io (see https://doc.rust-lang.org/cargo/reference/publishing.html for more info): 10 | ```shell 11 | cargo login 12 | # Only run the next steps if CI has run successfully. 13 | cargo publish -p ac-primitives 14 | cargo publish -p ac-keystore 15 | cargo publish -p ac-node-api 16 | cargo publish -p ac-compose-macros 17 | cargo publish -p substrate-api-client 18 | ``` 19 | 20 | ## Automatic Release Generation 21 | 22 | A new draft release gets generated upon a new tag with a name matching `v[0-9]+.[0-9]+.[0-9]+*` (E.g. v1.2.3 and v1.2.3-rc1) 23 | See the workflow: https://github.com/scs/substrate-api-client/blob/master/.github/workflows/draft-release.yml 24 | 25 | Example release: https://github.com/scs/substrate-api-client/releases/tag/v0.10.0 26 | 27 | The `🎉 Featuring` section has been created manually. It should show the user directly what has been updated and what new features have been added (not only PR names) 28 | 29 | ### PR Labels 30 | For automatic release generation, `E` and `F` labels are used. 31 | 32 | They have the following meaning: 33 | 34 | `E` labels (enforced by CI): 35 | - `E0-silent`: PR will not be mentioned at all in the release text. This should only be used for PRs that do not change any piece of functional code. E.g. CI and documentation updates. 36 | - `E1-breaksnothing`: PR will be listed in release text, no special release category. 37 | - `E1-breaksapi`: PR will be listed in release text in the category of `Breaking Changes`. Api-client users should pay special attention to this PR, as they most likely need to adapt their existing code. 38 | 39 | `F` labels: not enforced by CI, but some labels have a dedicated category in the release: 40 | - `F8-newfeature` and `F7-enhancement` labeled PRs are put in the category `🌈 Features` 41 | - `F2-bug` labeled PRs are put in the category `🐛 Bug Fixes` 42 | 43 | All PRs, except for `E0-silent` labaled ones, will be listed in the `Miscellaneous` category. 44 | 45 | Check out https://github.com/scs/substrate-api-client/blob/master/.github/release-drafter.yml for more information. 46 | 47 | 48 | ### Tag generation 49 | local tagging (ensure you're on the commit you want the tag to be on) 50 | ``` 51 | # Create local tag 52 | git tag # E.g. v0.10.0 53 | # Push to remote 54 | git push --tags 55 | ``` 56 | CI should now automatically create a draft release. This draft release needs to be released manually. 57 | 58 | ## Runtime wasm generation 59 | A new runtime wasm file for CI testing currently needs to be built locally. To do this the following steps need to be done: 60 | 1. Download a Polkadot / Substrate node. This can be any up to date node. The following is recommended because it's small and builds fast: https://github.com/paritytech/polkadot-sdk-minimal-template. But it does not include many pallets. 61 | 62 | 2. Update the runtime names and spec version. The `spec_name` and `impl_name` need to match the original runtime name of the running node. The `spec_version` needs to be higher than the original one. 63 | This needs to be adapted in the source code and looks like the code posted below. Often, it can be found in the `runtime/src/lib.rs` file (Example path minimal runtime: https://github.com/paritytech/polkadot-sdk-minimal-template/blob/master/runtime/src/lib.rs) 64 | ```rust 65 | /// The runtime version. 66 | #[runtime_version] 67 | pub const VERSION: RuntimeVersion = RuntimeVersion { 68 | spec_name: create_runtime_str!(""), 69 | impl_name: create_runtime_str!(""), 70 | authoring_version: 1, 71 | spec_version: "", 72 | impl_version: 1, 73 | apis: RUNTIME_API_VERSIONS, 74 | transaction_version: 1, 75 | state_version: 1, 76 | }; 77 | ``` 78 | 79 | 3. Build the runtime with cargo build. For the minimal runtime it is: `cargo build -p minimal-template-node --release` 80 | 81 | 82 | 4. Get the wasm file from the `target/release/wbuild/` folder. Example for the minimal runtime: `~/polkadot-sdk-minimal-template/target/release/wbuild/minimal-template-runtime/minimal_template_runtime.compact.compressed.wasm` 83 | 84 | ## Cargo.toml dependency Specification 85 | In the `Cargo.toml` we handle the versioning as following: 86 | - By default: No patch version specified 87 | - If there is a specific reason to specify a version with patch, then add a comment why this is needed (security vulnerability, ...) 88 | Cargo will ensure the imported version is not below the specified one (above is possible though). 89 | 90 | 91 | ## Code overview 92 |

93 | 94 |

95 | -------------------------------------------------------------------------------- /examples/async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-examples-async" 3 | version = "1.18.0" 4 | license = "Apache-2.0" 5 | edition = "2021" 6 | 7 | [dev-dependencies] 8 | codec = { workspace = true, features = ["std"] } 9 | env_logger = { workspace = true } 10 | log = { workspace = true } 11 | serde_json = { workspace = true, features = ["std"] } 12 | tokio = { workspace = true, features = ["rt-multi-thread", "macros", "time"] } 13 | tokio-util = { workspace = true } 14 | 15 | # Substrate dependencies 16 | frame-system = { workspace = true, features = ["std"] } 17 | rococo-runtime = { workspace = true, features = ["std"] } 18 | pallet-balances = { workspace = true, features = ["std"] } 19 | pallet-recovery = { workspace = true, features = ["std"] } 20 | pallet-staking = { workspace = true, features = ["std"] } 21 | sp-core = { workspace = true, features = ["std"] } 22 | sp-keyring = { workspace = true, features = ["std"] } 23 | sp-runtime = { workspace = true, features = ["std"] } 24 | sp-weights = { workspace = true, features = ["std"] } 25 | 26 | # local deps 27 | substrate-api-client = { workspace = true, features = ["std", "jsonrpsee-client", "staking-xt", "contracts-xt"] } 28 | -------------------------------------------------------------------------------- /examples/async/examples/benchmark_bulk_xt.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! This example floods the node with a series of transactions. 17 | 18 | use rococo_runtime::{BalancesCall, RuntimeCall}; 19 | use sp_keyring::Sr25519Keyring; 20 | use substrate_api_client::{ 21 | ac_primitives::{ 22 | Config, ExtrinsicSigner as GenericExtrinsicSigner, RococoRuntimeConfig, SignExtrinsic, 23 | }, 24 | rpc::JsonrpseeClient, 25 | Api, SubmitExtrinsic, 26 | }; 27 | 28 | // Define an extrinsic signer type which sets the generic types of the `GenericExtrinsicSigner`. 29 | // This way, the types don't have to be reassigned with every usage of this type and makes 30 | // the code better readable. 31 | type ExtrinsicSigner = GenericExtrinsicSigner; 32 | 33 | // To access the ExtrinsicAddress type of the Signer, we need to do this via the trait `SignExtrinsic`. 34 | // For better code readability, we define a simple type here and, at the same time, assign the 35 | // AccountId type of the `SignExtrinsic` trait. 36 | type ExtrinsicAddressOf = >::ExtrinsicAddress; 37 | 38 | // AccountId type of rococo runtime. 39 | type AccountId = ::AccountId; 40 | 41 | #[tokio::main] 42 | async fn main() { 43 | env_logger::init(); 44 | 45 | // Initialize api and set the signer (sender) that is used to sign the extrinsics. 46 | let signer = Sr25519Keyring::Alice.pair(); 47 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 48 | let mut api = Api::::new(client).await.unwrap(); 49 | api.set_signer(signer.into()); 50 | 51 | let recipient: ExtrinsicAddressOf = Sr25519Keyring::Bob.to_account_id().into(); 52 | // We use a manual nonce input here, because otherwise the api retrieves the nonce via getter and needs 53 | // to wait for the response of the node (and the actual execution of the previous extrinsic). 54 | // But because we want to spam the node with extrinsic, we simple monotonically increase the nonce, without 55 | // waiting for the response of the node. 56 | let mut nonce = api.get_nonce().await.unwrap(); 57 | let first_nonce = nonce; 58 | while nonce < first_nonce + 500 { 59 | // Compose a balance extrinsic. 60 | let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { 61 | dest: recipient.clone(), 62 | value: 1_000_000, 63 | }); 64 | let xt = api.compose_extrinsic_offline(call, nonce); 65 | 66 | println!("Sending extrinsic with nonce {}", nonce); 67 | let _tx_hash = api.submit_extrinsic(xt).await.unwrap(); 68 | 69 | nonce += 1; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /examples/async/examples/custom_nonce.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic 17 | //! without asking the node for nonce and does not need to know the metadata 18 | 19 | use rococo_runtime::{BalancesCall, RuntimeCall}; 20 | use sp_keyring::Sr25519Keyring; 21 | use sp_runtime::{generic::Era, MultiAddress}; 22 | use substrate_api_client::{ 23 | ac_primitives::{GenericAdditionalParams, RococoRuntimeConfig}, 24 | rpc::JsonrpseeClient, 25 | Api, Error, GetChainInfo, SubmitAndWatch, UnexpectedTxStatus, XtStatus, 26 | }; 27 | 28 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 29 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 30 | 31 | #[tokio::main] 32 | async fn main() { 33 | env_logger::init(); 34 | 35 | // Initialize api and set the signer (sender) that is used to sign the extrinsics. 36 | let signer = Sr25519Keyring::Alice.pair(); 37 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 38 | let mut api = Api::::new(client).await.unwrap(); 39 | api.set_signer(signer.into()); 40 | 41 | // Information for Era for mortal transactions. 42 | let last_finalized_header_hash = api.get_finalized_head().await.unwrap().unwrap(); 43 | let header = api.get_header(Some(last_finalized_header_hash)).await.unwrap().unwrap(); 44 | let period = 5; 45 | let tx_params = GenericAdditionalParams::new() 46 | .era(Era::mortal(period, header.number.into()), last_finalized_header_hash) 47 | .tip(0); 48 | 49 | // Set the custom additional params. 50 | api.set_additional_params(tx_params); 51 | 52 | // Get the nonce of Alice. 53 | let signer_nonce = api.get_nonce().await.unwrap(); 54 | println!("[+] Signer's Account Nonce is {}\n", signer_nonce); 55 | 56 | // Create an extrinsic that should get included in the future pool due to a nonce that is too high. 57 | let recipient = MultiAddress::Id(Sr25519Keyring::Bob.to_account_id()); 58 | let call = 59 | RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest: recipient, value: 42 }); 60 | let xt = api.compose_extrinsic_offline(call, signer_nonce + 1); 61 | println!("[+] Composed Extrinsic:\n {:?}\n", xt); 62 | 63 | // Send and watch extrinsic until InBlock. 64 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).await; 65 | println!("Returned Result {:?}", result); 66 | match result { 67 | Err(Error::UnexpectedTxStatus(UnexpectedTxStatus::Future)) => { 68 | // All good, we expected a Future Error. 69 | }, 70 | _ => panic!("Expected a future error"), 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/async/examples/get_blocks.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Very simple example that shows how to fetch chain information with async. 17 | //! To compile this example for async you need to set the `--no-default-features` flag 18 | 19 | use substrate_api_client::{ 20 | ac_primitives::RococoRuntimeConfig, 21 | rpc::{HandleSubscription, JsonrpseeClient}, 22 | Api, GetChainInfo, SubscribeChain, 23 | }; 24 | 25 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 26 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 27 | 28 | #[tokio::main] 29 | async fn main() { 30 | env_logger::init(); 31 | 32 | // Initialize the api. 33 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 34 | let api = Api::::new(client).await.unwrap(); 35 | 36 | let (genesis_block, header_hash, signed_block) = tokio::try_join!( 37 | api.get_genesis_block(), 38 | api.get_finalized_head(), 39 | api.get_finalized_block(), 40 | ) 41 | .unwrap(); 42 | let header_hash = header_hash.unwrap(); 43 | let signed_block = signed_block.unwrap(); 44 | println!("Genesis block: \n {:?} \n", genesis_block); 45 | println!("Latest Finalized Header Hash:\n {} \n", header_hash); 46 | 47 | let last_block_number = signed_block.block.header.number; 48 | // Get the previous three blocks of the last_block_number 49 | let number_of_last_three_blocks: Vec<_> = 50 | (last_block_number.saturating_sub(3)..last_block_number).collect(); 51 | 52 | let (header, blocks, latest_header, latest_block) = tokio::try_join!( 53 | api.get_header(Some(header_hash)), 54 | api.get_signed_blocks(&number_of_last_three_blocks), 55 | api.get_header(None), 56 | api.get_block(None), 57 | ) 58 | .unwrap(); 59 | println!("Finalized header:\n {:?} \n", header.unwrap()); 60 | println!("Finalized block:\n {:?} \n", signed_block); 61 | println!("Block numbers of the previous three blocks: "); 62 | for (i, b) in blocks.iter().enumerate() { 63 | println!(" Block {} has block number {}", i, b.block.header.number); 64 | } 65 | println!("Latest Header: \n {:?} \n", latest_header); 66 | println!("Latest block: \n {:?} \n", latest_block); 67 | 68 | println!("Subscribing to finalized heads"); 69 | let mut subscription = api.subscribe_finalized_heads().await.unwrap(); 70 | for _ in 0..5 { 71 | let head = subscription.next().await.unwrap().unwrap(); 72 | println!("Got new Block {:?}", head); 73 | println!("This print should be printed before the one with \"Got new Block\""); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/async/examples/minimal_template_runtime.compact.compressed.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scs/substrate-api-client/cd561c9c3f83c7037c449cdded270cbdcd9a6fc2/examples/async/examples/minimal_template_runtime.compact.compressed.wasm -------------------------------------------------------------------------------- /examples/async/examples/new_json_rpc_api_calls.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! This example shows how to call the unstable rpc api with self defined functions. 17 | //! This includes simple requests as well as subscription. 18 | 19 | use codec::Encode; 20 | use serde_json::Value; 21 | use sp_core::Bytes; 22 | use sp_keyring::Sr25519Keyring; 23 | use substrate_api_client::{ 24 | ac_compose_macros::rpc_params, 25 | ac_primitives::RococoRuntimeConfig, 26 | extrinsic::BalancesExtrinsics, 27 | rpc::{HandleSubscription, JsonrpseeClient, Request, Subscribe}, 28 | Api, 29 | }; 30 | 31 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 32 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 33 | 34 | #[tokio::main] 35 | async fn main() { 36 | env_logger::init(); 37 | 38 | // Initialize api and set the signer (sender) that is used to sign the extrinsics. 39 | let signer = Sr25519Keyring::Alice.pair(); 40 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 41 | let mut api = Api::::new(client).await.unwrap(); 42 | api.set_signer(signer.into()); 43 | 44 | // Retrieve all available rpc methods: 45 | let json_value: Value = api.client().request("rpc_methods", rpc_params![]).await.unwrap(); 46 | let json_string = serde_json::to_string(&json_value).unwrap(); 47 | println!("Available methods: {json_string} \n"); 48 | 49 | let chain_name_request = "chainSpec_v1_chainName"; 50 | let chain_genesis_hash_request = "chainSpec_v1_genesisHash"; 51 | let transaction_submit_watch = "transactionWatch_v1_submitAndWatch"; 52 | let transaction_unwatch = "transactionWatch_v1_unwatch"; 53 | 54 | // Submit the above defiend rpc requests: 55 | let chain_name: String = api.client().request(chain_name_request, rpc_params![]).await.unwrap(); 56 | println!("Our chain is called: {chain_name}"); 57 | 58 | let genesishash: String = 59 | api.client().request(chain_genesis_hash_request, rpc_params![]).await.unwrap(); 60 | println!("Chain genesis Hash: {genesishash}"); 61 | 62 | // Submit and watch a transaction: 63 | let bob = Sr25519Keyring::Bob.to_account_id(); 64 | let encoded_extrinsic: Bytes = api 65 | .balance_transfer_allow_death(bob.into(), 1000) 66 | .await 67 | .unwrap() 68 | .encode() 69 | .into(); 70 | 71 | let mut subscription = api 72 | .client() 73 | .subscribe::( 74 | transaction_submit_watch, 75 | rpc_params![encoded_extrinsic], 76 | transaction_unwatch, 77 | ) 78 | .await 79 | .unwrap(); 80 | while let Some(notification) = subscription.next().await { 81 | let notification = notification.unwrap(); 82 | println!("Subscription notification: {notification:?}"); 83 | let event_object_string = notification["event"].as_str().unwrap(); 84 | //let event_object_string = serde_json::from_string().unwrap(); 85 | match event_object_string { 86 | "finalized" => break, 87 | "bestChainBlockIncluded" | "validated" => println!("Got {event_object_string} event"), 88 | _ => panic!("Unexpected event: {event_object_string}"), 89 | }; 90 | } 91 | println!("Transaction got finalized, unsubscribing."); 92 | subscription.unsubscribe().await.unwrap(); 93 | } 94 | -------------------------------------------------------------------------------- /examples/async/examples/print_metadata.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Very simple example that shows how to pretty print the metadata. Has proven to be a helpful 17 | //! debugging tool. 18 | 19 | use substrate_api_client::{ 20 | ac_primitives::RococoRuntimeConfig, api_client::UpdateRuntime, rpc::JsonrpseeClient, Api, 21 | }; 22 | 23 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 24 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 25 | 26 | #[tokio::main] 27 | async fn main() { 28 | env_logger::init(); 29 | 30 | // Initialize the api, which retrieves the metadata from the node upon initialization. 31 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 32 | let mut api = Api::::new(client).await.unwrap(); 33 | 34 | let meta = api.metadata().clone(); 35 | 36 | meta.print_overview(); 37 | meta.print_pallets(); 38 | meta.print_pallets_with_calls(); 39 | meta.print_pallets_with_events(); 40 | meta.print_pallets_with_errors(); 41 | meta.print_pallets_with_constants(); 42 | 43 | // Update the runtime and metadata. 44 | api.update_runtime().await.unwrap(); 45 | 46 | // Print full substrate metadata json formatted. 47 | println!("{}", (&api.metadata().pretty_format().unwrap())) 48 | } 49 | -------------------------------------------------------------------------------- /examples/async/examples/query_runtime_api.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Very simple example that shows how to query Runtime Api of a Substrate node. 17 | 18 | use codec::Encode; 19 | use sp_core::sr25519; 20 | use sp_keyring::Sr25519Keyring; 21 | use substrate_api_client::{ 22 | ac_primitives::RococoRuntimeConfig, 23 | extrinsic::BalancesExtrinsics, 24 | rpc::JsonrpseeClient, 25 | runtime_api::{AuthorityDiscoveryApi, CoreApi, MetadataApi, RuntimeApi, TransactionPaymentApi}, 26 | Api, GetChainInfo, 27 | }; 28 | 29 | const UNSTABLE_METADATA_VERSION: u32 = u32::MAX; 30 | 31 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 32 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 33 | 34 | #[tokio::main] 35 | async fn main() { 36 | env_logger::init(); 37 | 38 | // Initialize the api, which retrieves the metadata from the node upon initialization. 39 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 40 | let mut api = Api::::new(client).await.unwrap(); 41 | let alice_pair = Sr25519Keyring::Alice.pair(); 42 | api.set_signer(alice_pair.into()); 43 | let runtime_api = api.runtime_api(); 44 | 45 | // Query the fee of an extrinsic. 46 | let bob = Sr25519Keyring::Bob.to_account_id(); 47 | let balance_extrinsic = 48 | api.balance_transfer_allow_death(bob.clone().into(), 1000).await.unwrap(); 49 | let extrinsic_fee_details = runtime_api 50 | .query_fee_details(balance_extrinsic.clone(), 1000, None) 51 | .await 52 | .unwrap(); 53 | let final_fee = extrinsic_fee_details.final_fee(); 54 | println!("To exceute the balance extrinsic, the following fee is required: {:?}", final_fee); 55 | 56 | // Get the authority Ids. 57 | let authority_ids: Vec = runtime_api.authorities(None).await.unwrap(); 58 | println!("The following authorities are currently active:"); 59 | for authority in authority_ids { 60 | println!("{:?}", authority); 61 | } 62 | 63 | // Query the runtime api version. 64 | let version = runtime_api.version(None).await.unwrap(); 65 | println!("{:?}", version); 66 | 67 | // Query the available metadata versions. 68 | let metadata_versions = runtime_api.metadata_versions(None).await.unwrap(); 69 | assert_eq!(metadata_versions, [14, 15, UNSTABLE_METADATA_VERSION]); 70 | 71 | // List all apis and functions thereof. 72 | let trait_names = runtime_api.list_traits(None).await.unwrap(); 73 | println!(); 74 | println!("Available traits:"); 75 | for name in trait_names { 76 | println!("{name}"); 77 | } 78 | println!(); 79 | 80 | let trait_name = "BabeApi"; 81 | let method_names = runtime_api.list_methods_of_trait(trait_name, None).await.unwrap(); 82 | println!("Available methods of {trait_name}:"); 83 | for name in method_names { 84 | println!("{name}"); 85 | } 86 | println!(); 87 | 88 | // Create your own runtime api call. 89 | let parameters = vec![1000.encode()]; 90 | let latest_block_hash = api.get_block_hash(None).await.unwrap().unwrap(); 91 | let result: Result = runtime_api 92 | .runtime_call( 93 | "TransactionPaymentApi_query_length_to_fee", 94 | parameters, 95 | Some(latest_block_hash), 96 | ) 97 | .await; 98 | let output = result.unwrap(); 99 | println!("Received the following output: {:?}", output); 100 | } 101 | -------------------------------------------------------------------------------- /examples/async/examples/runtime_update_async.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | //! Example that shows how to detect a runtime update and afterwards update the metadata. 15 | 16 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 17 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 18 | 19 | use sp_keyring::Sr25519Keyring; 20 | use sp_weights::Weight; 21 | use substrate_api_client::{ 22 | ac_compose_macros::{compose_call, compose_extrinsic}, 23 | ac_primitives::{Config, RococoRuntimeConfig}, 24 | api_client::UpdateRuntime, 25 | rpc::JsonrpseeClient, 26 | rpc_api::RuntimeUpdateDetector, 27 | Api, SubmitAndWatch, SubscribeEvents, XtStatus, 28 | }; 29 | use tokio::select; 30 | use tokio_util::sync::CancellationToken; 31 | 32 | type Hash = ::Hash; 33 | 34 | #[tokio::main] 35 | async fn main() { 36 | env_logger::init(); 37 | 38 | // Initialize the api. 39 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 40 | let mut api = Api::::new(client).await.unwrap(); 41 | let sudoer = Sr25519Keyring::Alice.pair(); 42 | api.set_signer(sudoer.into()); 43 | 44 | let subscription = api.subscribe_events().await.unwrap(); 45 | let mut update_detector: RuntimeUpdateDetector = 46 | RuntimeUpdateDetector::new(subscription); 47 | println!("Current spec_version: {}", api.spec_version()); 48 | 49 | // Create future that informs about runtime update events 50 | let detector_future = update_detector.detect_runtime_update(); 51 | 52 | let token = CancellationToken::new(); 53 | let cloned_token = token.clone(); 54 | 55 | // To prevent blocking forever we create another future that cancels the 56 | // wait after some time 57 | tokio::spawn(async move { 58 | tokio::time::sleep(std::time::Duration::from_secs(20)).await; 59 | cloned_token.cancel(); 60 | println!("Cancelling wait for runtime update"); 61 | }); 62 | 63 | send_code_update_extrinsic(&api).await; 64 | 65 | // Wait for one of the futures to resolve and check which one resolved 66 | let runtime_update_detected = select! { 67 | _ = token.cancelled() => { 68 | false 69 | }, 70 | _ = detector_future => { 71 | api.update_runtime().await.unwrap(); 72 | true 73 | }, 74 | }; 75 | println!("Detected runtime update: {runtime_update_detected}"); 76 | println!("New spec_version: {}", api.spec_version()); 77 | assert!(api.spec_version() == 111111111); 78 | assert!(runtime_update_detected); 79 | } 80 | 81 | pub async fn send_code_update_extrinsic( 82 | api: &substrate_api_client::Api, 83 | ) { 84 | let new_wasm: &[u8] = include_bytes!("minimal_template_runtime.compact.compressed.wasm"); 85 | 86 | // this call can only be called by sudo 87 | let call = compose_call!(api.metadata(), "System", "set_code", new_wasm.to_vec()).unwrap(); 88 | let weight: Weight = 0.into(); 89 | let xt = compose_extrinsic!(&api, "Sudo", "sudo_unchecked_weight", call, weight).unwrap(); 90 | 91 | println!("Sending extrinsic to trigger runtime update"); 92 | let block_hash = api 93 | .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) 94 | .await 95 | .unwrap() 96 | .block_hash 97 | .unwrap(); 98 | println!("[+] Extrinsic got included. Block Hash: {:?}", block_hash); 99 | } 100 | -------------------------------------------------------------------------------- /examples/async/examples/subscribe_events.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Example that shows how to subscribe to events and do some action 17 | //! upon encounterign them. 18 | 19 | use log::debug; 20 | use sp_core::H256 as Hash; 21 | use substrate_api_client::{ 22 | ac_primitives::RococoRuntimeConfig, rpc::JsonrpseeClient, Api, SubscribeEvents, 23 | }; 24 | 25 | // This module depends on the specific node runtime. 26 | // Replace this crate by your own if you run a custom substrate node to get your custom events. 27 | use rococo_runtime::RuntimeEvent; 28 | 29 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 30 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 31 | 32 | #[tokio::main] 33 | async fn main() { 34 | env_logger::init(); 35 | 36 | // Initialize the api. 37 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 38 | let api = Api::::new(client).await.unwrap(); 39 | 40 | println!("Subscribe to events"); 41 | let mut subscription = api.subscribe_events().await.unwrap(); 42 | 43 | // Wait for event callbacks from the node, which are received via subscription. 44 | for _ in 0..5 { 45 | let event_records = 46 | subscription.next_events::().await.unwrap().unwrap(); 47 | for event_record in &event_records { 48 | println!("decoded: {:?} {:?}", event_record.phase, event_record.event); 49 | match &event_record.event { 50 | RuntimeEvent::Balances(balances_event) => { 51 | println!(">>>>>>>>>> balances event: {:?}", balances_event); 52 | match &balances_event { 53 | pallet_balances::Event::Transfer { from, to, amount } => { 54 | println!("Transactor: {:?}", from); 55 | println!("Destination: {:?}", to); 56 | println!("Value: {:?}", amount); 57 | return 58 | }, 59 | _ => { 60 | debug!("ignoring unsupported balances event"); 61 | }, 62 | } 63 | }, 64 | RuntimeEvent::System(system_event) => { 65 | println!(">>>>>>>>>> system event: {:?}", system_event); 66 | match &system_event { 67 | frame_system::Event::ExtrinsicSuccess { dispatch_info } => { 68 | println!("DispatchInfo: {:?}", dispatch_info); 69 | return 70 | }, 71 | _ => { 72 | debug!("ignoring unsupported system event"); 73 | }, 74 | } 75 | }, 76 | _ => debug!("ignoring unsupported module event: {:?}", event_record.event), 77 | } 78 | } 79 | } 80 | 81 | // After we finished whatever we wanted, unusubscribe from the subscription, 82 | // to ensure, that the node does not keep sending us events. 83 | subscription.unsubscribe().await.unwrap(); 84 | } 85 | -------------------------------------------------------------------------------- /examples/async/examples/sudo.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! This example shows how to use the compose_extrinsic macro to create an extrinsic for any (custom) 17 | //! module, whereas the desired module and call are supplied as a string. 18 | 19 | use codec::Compact; 20 | use sp_keyring::Sr25519Keyring; 21 | use substrate_api_client::{ 22 | ac_compose_macros::{compose_call, compose_extrinsic}, 23 | ac_primitives::{ 24 | Config, ExtrinsicSigner as GenericExtrinsicSigner, RococoRuntimeConfig, SignExtrinsic, 25 | UncheckedExtrinsic, 26 | }, 27 | rpc::JsonrpseeClient, 28 | Api, GetAccountInformation, SubmitAndWatch, XtStatus, 29 | }; 30 | 31 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 32 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 33 | 34 | // Define an extrinsic signer type which sets the generic types of the `GenericExtrinsicSigner`. 35 | // This way, the types don't have to be reassigned with every usage of this type and makes 36 | // the code better readable. 37 | type ExtrinsicSigner = GenericExtrinsicSigner; 38 | 39 | // To access the ExtrinsicAddress type of the Signer, we need to do this via the trait `SignExtrinsic`. 40 | // For better code readability, we define a simple type here and, at the same time, assign the 41 | // AccountId type of the `SignExtrinsic` trait. 42 | type ExtrinsicAddressOf = >::ExtrinsicAddress; 43 | 44 | // AccountId type of rococo runtime. 45 | type AccountId = ::AccountId; 46 | type Address = ::Address; 47 | 48 | #[tokio::main] 49 | async fn main() { 50 | env_logger::init(); 51 | 52 | // Initialize api and set the signer (sender) that is used to sign the extrinsics. 53 | let sudoer = Sr25519Keyring::Alice.pair(); 54 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 55 | let mut api = Api::::new(client).await.unwrap(); 56 | api.set_signer(sudoer.into()); 57 | 58 | // Set the recipient of newly issued funds. 59 | let recipient = Sr25519Keyring::Bob.to_account_id(); 60 | 61 | // Get the current balance of the recipient. 62 | let recipient_balance = api.get_account_data(&recipient).await.unwrap().unwrap().free; 63 | println!("[+] Recipients's Free Balance is now {}\n", recipient_balance); 64 | 65 | // Compose a call that should only be executable via Sudo. 66 | let recipients_extrinsic_address: ExtrinsicAddressOf = 67 | recipient.clone().into(); 68 | let new_balance = recipient_balance + 100; 69 | let call: ([u8; 2], Address, Compact<_>) = compose_call!( 70 | api.metadata(), 71 | "Balances", 72 | "force_set_balance", 73 | recipients_extrinsic_address, 74 | Compact(new_balance) 75 | ) 76 | .unwrap(); 77 | 78 | let xt: UncheckedExtrinsic<_, _, _, _> = 79 | compose_extrinsic!(&api, "Sudo", "sudo", call).unwrap(); 80 | 81 | // Send and watch extrinsic until in block. 82 | let block_hash = api 83 | .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) 84 | .await 85 | .unwrap() 86 | .block_hash; 87 | println!("[+] Extrinsic got included. Block Hash: {:?}", block_hash); 88 | 89 | // Ensure the extrinsic has been executed. 90 | let recipient_new_balance = api.get_account_data(&recipient).await.unwrap().unwrap().free; 91 | assert_eq!(recipient_new_balance, new_balance); 92 | } 93 | -------------------------------------------------------------------------------- /examples/sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-examples-sync" 3 | version = "1.18.0" 4 | license = "Apache-2.0" 5 | edition = "2021" 6 | 7 | [dev-dependencies] 8 | env_logger = "0.11" 9 | log = "0.4" 10 | 11 | # Substrate dependencies 12 | sp-core = { workspace = true } 13 | sp-keyring = { workspace = true } 14 | sp-runtime = { workspace = true } 15 | sp-weights = { workspace = true } 16 | 17 | # local deps 18 | substrate-api-client = { workspace = true, features = ["tungstenite-client", "ws-client"] } 19 | -------------------------------------------------------------------------------- /examples/sync/examples/minimal_template_runtime.compact.compressed.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scs/substrate-api-client/cd561c9c3f83c7037c449cdded270cbdcd9a6fc2/examples/sync/examples/minimal_template_runtime.compact.compressed.wasm -------------------------------------------------------------------------------- /examples/sync/examples/runtime_update_sync.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | //! Example that shows how to detect a runtime update and afterwards update the metadata. 15 | 16 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 17 | // own runtime if it uses different parameter configurations. Several rre-compiled runtimes are available in the ac-primitives crate. 18 | 19 | use core::{ 20 | sync::atomic::{AtomicBool, Ordering}, 21 | time::Duration, 22 | }; 23 | use sp_keyring::Sr25519Keyring; 24 | use sp_weights::Weight; 25 | use std::{sync::Arc, thread}; 26 | use substrate_api_client::{ 27 | ac_compose_macros::{compose_call, compose_extrinsic}, 28 | ac_primitives::{Config, RococoRuntimeConfig}, 29 | api_client::UpdateRuntime, 30 | rpc::TungsteniteRpcClient, 31 | rpc_api::RuntimeUpdateDetector, 32 | Api, SubmitAndWatch, SubscribeEvents, XtStatus, 33 | }; 34 | 35 | type Hash = ::Hash; 36 | 37 | fn main() { 38 | env_logger::init(); 39 | 40 | // Initialize the api. 41 | let client = TungsteniteRpcClient::with_default_url(1); 42 | let mut api = Api::::new(client).unwrap(); 43 | let sudoer = Sr25519Keyring::Alice.pair(); 44 | api.set_signer(sudoer.into()); 45 | 46 | let subscription = api.subscribe_events().unwrap(); 47 | let cancellation = Arc::new(AtomicBool::new(false)); 48 | let mut update_detector: RuntimeUpdateDetector = 49 | RuntimeUpdateDetector::new_with_cancellation(subscription, cancellation.clone()); 50 | 51 | println!("Current spec_version: {}", api.spec_version()); 52 | 53 | let handler = thread::spawn(move || { 54 | // Wait for potential runtime update events 55 | let runtime_update_detected = update_detector.detect_runtime_update().unwrap(); 56 | println!("Detected runtime update: {runtime_update_detected}"); 57 | assert!(runtime_update_detected); 58 | }); 59 | 60 | // Execute an actual runtime update 61 | { 62 | send_code_update_extrinsic(&api); 63 | } 64 | 65 | // Sleep for some time in order to wait for a runtime update 66 | // If no update happens we cancel the wait 67 | { 68 | thread::sleep(Duration::from_secs(1)); 69 | cancellation.store(true, Ordering::SeqCst); 70 | } 71 | 72 | handler.join().unwrap(); 73 | api.update_runtime().unwrap(); 74 | println!("New spec_version: {}", api.spec_version()); 75 | assert!(api.spec_version() == 111111111); 76 | } 77 | 78 | pub fn send_code_update_extrinsic( 79 | api: &substrate_api_client::Api, 80 | ) { 81 | let new_wasm: &[u8] = include_bytes!("minimal_template_runtime.compact.compressed.wasm"); 82 | 83 | // Create a sudo `set_code` call. 84 | let call = compose_call!(api.metadata(), "System", "set_code", new_wasm.to_vec()).unwrap(); 85 | let weight: Weight = 0.into(); 86 | let xt = compose_extrinsic!(&api, "Sudo", "sudo_unchecked_weight", call, weight).unwrap(); 87 | 88 | println!("Sending extrinsic to trigger runtime update"); 89 | let block_hash = api 90 | .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) 91 | .unwrap() 92 | .block_hash 93 | .unwrap(); 94 | println!("[+] Extrinsic got included. Block Hash: {block_hash:?}"); 95 | } 96 | -------------------------------------------------------------------------------- /examples/sync/examples/transfer_with_tungstenite_client.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Very simple example that shows how to use a predefined extrinsic from the extrinsic module. 17 | 18 | use sp_core::{ 19 | crypto::{Pair, Ss58Codec}, 20 | sr25519, 21 | }; 22 | use sp_runtime::MultiAddress; 23 | use substrate_api_client::{ 24 | ac_primitives::RococoRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::TungsteniteRpcClient, 25 | Api, GetAccountInformation, SubmitAndWatch, XtStatus, 26 | }; 27 | 28 | // To test this example with CI we run it against the Polkadot Rococo node. Remember to switch the Config to match your 29 | // own runtime if it uses different parameter configurations. Several pre-compiled runtimes are available in the ac-primitives crate. 30 | 31 | fn main() { 32 | env_logger::init(); 33 | 34 | // Alice's seed: subkey inspect //Alice. 35 | let alice: sr25519::Pair = Pair::from_string( 36 | "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a", 37 | None, 38 | ) 39 | .unwrap(); 40 | println!("signer account: {}", alice.public().to_ss58check()); 41 | 42 | // Initialize api and set the signer (sender) that is used to sign the extrinsics. 43 | let client = TungsteniteRpcClient::with_default_url(100); 44 | let mut api = Api::::new(client).unwrap(); 45 | api.set_signer(alice.clone().into()); 46 | 47 | // Retrieve bobs current balance. 48 | let bob = sr25519::Public::from_ss58check("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty") 49 | .unwrap(); 50 | 51 | let bob_balance = api.get_account_data(&bob.into()).unwrap().unwrap_or_default().free; 52 | println!("[+] Bob's Free Balance is {bob_balance}\n"); 53 | 54 | // We first generate an extrinsic that will fail to be executed due to missing funds. 55 | let xt = api 56 | .balance_transfer_allow_death(MultiAddress::Id(bob.into()), bob_balance + 1) 57 | .unwrap(); 58 | println!( 59 | "Sending an extrinsic from Alice (Key = {}),\n\nto Bob (Key = {})\n", 60 | alice.public(), 61 | bob 62 | ); 63 | println!("[+] Composed extrinsic: {xt:?}\n"); 64 | 65 | // Send and watch extrinsic until it fails onchain. 66 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock); 67 | assert!(result.is_err()); 68 | println!("[+] Extrinsic did not get included due to: {result:?}\n"); 69 | 70 | // This time, we generate an extrinsic that will succeed. 71 | let xt = api 72 | .balance_transfer_allow_death(MultiAddress::Id(bob.into()), bob_balance / 2) 73 | .unwrap(); 74 | println!( 75 | "Sending an extrinsic from Alice (Key = {}),\n\nto Bob (Key = {})\n", 76 | alice.public(), 77 | bob 78 | ); 79 | println!("[+] Composed extrinsic: {xt:?}\n"); 80 | 81 | // Send and watch extrinsic until in block. 82 | let block_hash = api 83 | .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) 84 | .unwrap() 85 | .block_hash 86 | .unwrap(); 87 | println!("[+] Extrinsic got included. Block Hash: {block_hash:?}\n"); 88 | 89 | // Verify that Bob's free Balance increased. 90 | let bob_new_balance = api.get_account_data(&bob.into()).unwrap().unwrap().free; 91 | println!("[+] Bob's Free Balance is now {bob_new_balance}\n"); 92 | assert!(bob_new_balance > bob_balance); 93 | } 94 | -------------------------------------------------------------------------------- /examples/wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-examples-wasm" 3 | version = "1.18.0" 4 | license = "Apache-2.0" 5 | edition = "2021" 6 | 7 | [dev-dependencies] 8 | pallet-balances = { workspace = true } 9 | sp-core = { workspace = true, features = ["full_crypto", "serde"] } 10 | sp-runtime = { workspace = true } 11 | substrate-api-client = { workspace = true } 12 | -------------------------------------------------------------------------------- /examples/wasm/examples/wasm_example.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | //! Example that some basic functions that can be executed in WebAssembly. 15 | 16 | pub use pallet_balances::Call as BalancesCall; 17 | use sp_core::{ 18 | crypto::{AccountId32, Ss58Codec}, 19 | sr25519, Pair, 20 | }; 21 | use sp_runtime::MultiAddress; 22 | pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; 23 | use std::process::ExitCode; 24 | use substrate_api_client::ac_primitives::{ExtrinsicSigner, RococoRuntimeConfig}; 25 | 26 | fn main() -> Result { 27 | // This test is not yet very sophisticated and not exhaustive. 28 | // Still it shows how some basic data structures can be constructed and used. 29 | let alice: sr25519::Pair = Pair::from_string( 30 | "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a", 31 | None, 32 | ) 33 | .unwrap(); 34 | 35 | let bob_account: AccountId32 = 36 | sr25519::Public::from_ss58check("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty") 37 | .unwrap() 38 | .into(); 39 | let _bob: MultiAddress = MultiAddress::Id(bob_account); 40 | let es_converted: ExtrinsicSigner = alice.clone().into(); 41 | let es_new = ExtrinsicSigner::::new(alice.clone()); 42 | assert_eq!(es_converted.signer().public(), es_new.signer().public()); 43 | 44 | let extrinsic = UncheckedExtrinsic::from_bytes(&[]); 45 | if extrinsic.is_ok() { 46 | panic!("Extrinsic should be invalid") 47 | } 48 | Ok(ExitCode::from(0)) 49 | } 50 | -------------------------------------------------------------------------------- /keystore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-keystore" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/scs/substrate-api-client" 8 | description = "Keystore (and session key management) for ed25519 based chains like Polkadot. (fork of sc-keystore)" 9 | readme = "README.md" 10 | 11 | [dependencies] 12 | array-bytes = { workspace = true } 13 | parking_lot = { workspace = true } 14 | serde_json = { workspace = true, features = ["std"] } 15 | 16 | # Substrate dependencies 17 | sc-keystore = { workspace = true } 18 | sp-application-crypto = { workspace = true, features = ["std"] } 19 | sp-core = { workspace = true, features = ["std"] } 20 | sp-keystore = { workspace = true, features = ["std"] } 21 | 22 | [dev-dependencies] 23 | tempfile = "3.3" 24 | -------------------------------------------------------------------------------- /keystore/README.md: -------------------------------------------------------------------------------- 1 | # ac_keystore 2 | 3 | This crate is a submodule of the [substrate-api-client](https://github.com/scs/substrate-api-client). It is a fork of substrate's sc-keystore crate. The substrate-api-client needs its own fork in order to access private functions and members of the crate. 4 | -------------------------------------------------------------------------------- /keystore/src/keystore_ext.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | use crate::LocalKeystore; 17 | use sc_keystore::Result; 18 | use sp_application_crypto::{AppPair, AppPublic}; 19 | 20 | /// This is an extension from the substrate-api-client repo. Keep it as a separate trait to 21 | /// make that clear. 22 | pub trait KeystoreExt { 23 | fn generate(&self) -> Result; 24 | fn public_keys(&self) -> Result>; 25 | } 26 | 27 | impl KeystoreExt for LocalKeystore { 28 | fn generate(&self) -> Result { 29 | self.0.write().generate_by_type::(Pair::ID).map(Into::into) 30 | } 31 | 32 | fn public_keys(&self) -> Result> { 33 | self.0 34 | .read() 35 | .raw_public_keys(Public::ID) 36 | .map(|v| v.into_iter().filter_map(|k| Public::from_slice(k.as_slice()).ok()).collect()) 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use crate::{KeystoreExt, LocalKeystore}; 43 | use sp_application_crypto::sr25519::AppPair; 44 | use sp_core::Pair; 45 | 46 | #[test] 47 | fn test_execute_generate_doesnt_fail() { 48 | let store = LocalKeystore::in_memory(); 49 | let generated_key = store.generate::(); 50 | 51 | // check that something was generated 52 | assert_ne!("", format!("{:?}", generated_key.unwrap().to_raw_vec())); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ksm_metadata_v14.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scs/substrate-api-client/cd561c9c3f83c7037c449cdded270cbdcd9a6fc2/ksm_metadata_v14.bin -------------------------------------------------------------------------------- /license_header_scs.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ -------------------------------------------------------------------------------- /node-api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-node-api" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/scs/substrate-api-client" 8 | description = "Substrate node type definitions and helpers for the substrate-api-client" 9 | readme = "README.md" 10 | categories = ["no-std"] 11 | 12 | [dependencies] 13 | bitvec = { workspace = true } 14 | codec = { workspace = true } 15 | derive_more = { workspace = true } 16 | either = { workspace = true } 17 | frame-metadata = { workspace = true, features = ["current", "serde_full", "decode"] } 18 | hex = { workspace = true } 19 | log = { workspace = true } 20 | scale-decode = { workspace = true, features = ["primitive-types", "derive"] } 21 | scale-encode = { workspace = true, features = ["bits", "primitive-types", "derive"] } 22 | scale-info = { workspace = true, features = ["derive", "decode", "bitvec"] } 23 | scale-value = { workspace = true } 24 | serde = { workspace = true, features = ["derive"] } 25 | serde_json = { workspace = true, features = ["alloc"] } 26 | 27 | # substrate 28 | sp-core = { workspace = true, features = ["full_crypto", "serde"] } 29 | sp-crypto-hashing = { workspace = true } 30 | sp-runtime = { workspace = true, features = ["serde"] } 31 | sp-storage = { workspace = true, features = ["serde"] } 32 | 33 | # need to add this for `no_std` 34 | sp-application-crypto = { workspace = true, features = ["full_crypto"] } 35 | sp-runtime-interface = { workspace = true } 36 | 37 | [dev-dependencies] 38 | test-case = { workspace = true } 39 | 40 | [features] 41 | default = ["std"] 42 | # To support `no_std` builds in non-32 bit environments. 43 | disable_target_static_assertions = [ 44 | "sp-runtime-interface/disable_target_static_assertions", 45 | ] 46 | std = [ 47 | "bitvec/std", 48 | "codec/std", 49 | "either/default", 50 | "frame-metadata/std", 51 | "hex/std", 52 | "log/std", 53 | "scale-info/std", 54 | "serde/std", 55 | "serde_json/std", 56 | # substrate 57 | "sp-core/std", 58 | "sp-runtime/std", 59 | # no_std support 60 | "sp-application-crypto/std", 61 | "sp-runtime-interface/std", 62 | "sp-storage/std", 63 | ] 64 | # Enable import of test_utils also outside of crate (feature test does not allow to do so). 65 | mocks = [] 66 | -------------------------------------------------------------------------------- /node-api/README.md: -------------------------------------------------------------------------------- 1 | # ac_node_api 2 | 3 | This crate is a submodule of the [substrate-api-client](https://github.com/scs/substrate-api-client). It contains Substrate node specific types and helpers such as: 4 | - Strorage keys (key hash, double map, ..) 5 | - Metadata 6 | - Runtime Events 7 | - Runtime Errors 8 | -------------------------------------------------------------------------------- /node-api/src/error/mod.rs: -------------------------------------------------------------------------------- 1 | // This file was taken from subxt (Parity Technologies (UK)) 2 | // https://github.com/paritytech/subxt/ 3 | // And was adapted by Supercomputing Systems AG. 4 | // 5 | // Copyright 2019-2022 Parity Technologies (UK) Ltd, Supercomputing Systems AG. 6 | // This file is licensed as Apache-2.0 7 | // see LICENSE for license details. 8 | 9 | //! General node-api Error implementation. 10 | 11 | use alloc::{boxed::Box, string::String, vec::Vec}; 12 | use core::fmt::Debug; 13 | use derive_more::From; 14 | 15 | // Re-expose the errors we use from other crates here: 16 | pub use crate::metadata::{MetadataConversionError, MetadataError}; 17 | pub use scale_decode::{visitor::DecodeError as VisitorDecodeError, Error as DecodeError}; 18 | pub use scale_encode::Error as EncodeError; 19 | pub use sp_core::crypto::SecretStringError; 20 | pub use sp_runtime::transaction_validity::TransactionValidityError; 21 | 22 | mod dispatch_error; 23 | pub use dispatch_error::*; 24 | 25 | /// The underlying error enum, generic over the type held by the `Runtime` 26 | /// variant. Prefer to use the [`Error`] and [`Error`] aliases over 27 | /// using this type directly. 28 | #[derive(Debug, From)] 29 | pub enum Error { 30 | /// Codec error. 31 | Codec(codec::Error), 32 | /// Serde serialization error 33 | Serialization(serde_json::error::Error), 34 | /// Secret string error. 35 | SecretString(SecretStringError), 36 | /// Invalid metadata error 37 | InvalidMetadata(MetadataConversionError), 38 | /// Invalid metadata error 39 | Metadata(MetadataError), 40 | /// Runtime error. 41 | Runtime(DispatchError), 42 | /// Error decoding to a [`crate::dynamic::Value`]. 43 | DecodeValue(Box), 44 | /// Error encoding from a [`crate::dynamic::Value`]. 45 | EncodeValue(Box), 46 | /// Visitor Decode Error. 47 | Visitor(VisitorDecodeError), 48 | /// The bytes representing an error that we were unable to decode. 49 | Unknown(Vec), 50 | /// Other error. 51 | Other(String), 52 | } 53 | 54 | impl From<&str> for Error { 55 | fn from(error: &str) -> Self { 56 | Error::Other(error.into()) 57 | } 58 | } 59 | 60 | impl From for Error { 61 | fn from(error: DecodeError) -> Self { 62 | Error::DecodeValue(Box::new(error)) 63 | } 64 | } 65 | 66 | impl From for Error { 67 | fn from(error: EncodeError) -> Self { 68 | Error::EncodeValue(Box::new(error)) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /node-api/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Integritee AG and Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | //! Contains stuff to instantiate communication with a substrate node. 15 | 16 | #![cfg_attr(not(feature = "std"), no_std)] 17 | 18 | extern crate alloc; 19 | 20 | use alloc::{borrow::ToOwned, vec::Vec}; 21 | use codec::{Decode, Encode}; 22 | 23 | pub use alloc::{collections::BTreeMap, vec}; 24 | pub use events::{EventDetails, Events, RawEventDetails}; 25 | pub use metadata::{Metadata, MetadataError}; 26 | pub use scale_decode::DecodeAsType; 27 | 28 | pub mod error; 29 | pub mod events; 30 | pub mod metadata; 31 | pub mod storage; 32 | 33 | #[cfg(any(feature = "mocks", test))] 34 | pub mod test_utils; 35 | 36 | /// Wraps an already encoded byte vector, prevents being encoded as a raw byte vector as part of 37 | /// the transaction payload 38 | #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] 39 | pub struct Encoded(pub Vec); 40 | 41 | impl codec::Encode for Encoded { 42 | fn encode(&self) -> Vec { 43 | self.0.to_owned() 44 | } 45 | } 46 | 47 | // This following types were taken from subxt (Parity Technologies (UK)) 48 | // https://github.com/paritytech/subxt/ 49 | 50 | /// Trait to uniquely identify the events's identity from the runtime metadata. 51 | /// 52 | /// Generated API structures that represent an event implement this trait. 53 | /// 54 | /// The trait is utilized to decode emitted events from a block, via obtaining the 55 | /// form of the `Event` from the metadata. 56 | pub trait StaticEvent: Decode { 57 | /// Pallet name. 58 | const PALLET: &'static str; 59 | /// Event name. 60 | const EVENT: &'static str; 61 | 62 | /// Returns true if the given pallet and event names match this event. 63 | fn is_event(pallet: &str, event: &str) -> bool { 64 | Self::PALLET == pallet && Self::EVENT == event 65 | } 66 | } 67 | 68 | /// A phase of a block's execution. 69 | // https://github.com/paritytech/substrate/blob/2bfc1dd66ef32cf8beb90007dfb544a9d28f1b2f/frame/system/src/lib.rs#L698-L708 70 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Encode, Decode)] 71 | pub enum Phase { 72 | /// Applying an extrinsic. 73 | ApplyExtrinsic(u32), 74 | /// Finalizing the block. 75 | Finalization, 76 | /// Initializing the block. 77 | Initialization, 78 | } 79 | 80 | /// Record of an event happening. 81 | // https://github.com/paritytech/substrate/blob/2bfc1dd66ef32cf8beb90007dfb544a9d28f1b2f/frame/system/src/lib.rs#L716-L726 82 | #[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)] 83 | pub struct EventRecord { 84 | /// The phase of the block it happened in. 85 | pub phase: Phase, 86 | /// The event itself. 87 | pub event: E, 88 | /// The list of the topics this event has. 89 | pub topics: Vec, 90 | } 91 | -------------------------------------------------------------------------------- /node-api/src/metadata/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use alloc::string::String; 15 | use codec::{Decode, Encode}; 16 | 17 | /// Metadata error originated from inspecting the internal representation of the runtime metadata. 18 | #[derive(Debug, Clone, PartialEq, Eq)] 19 | pub enum MetadataError { 20 | /// The DispatchError type isn't available in the metadata. 21 | DispatchErrorNotFound, 22 | /// Module is not in metadata. 23 | PalletNameNotFound(String), 24 | /// Pallet is not in metadata. 25 | PalletIndexNotFound(u8), 26 | /// Event type not found in metadata. 27 | EventTypeNotFoundInPallet(u8), 28 | /// Call is not in metadata. 29 | CallNotFound(&'static str), 30 | /// Event is not in metadata. 31 | EventNotFound(u8, u8), 32 | /// Error is not in metadata. 33 | ErrorNotFound(u8, u8), 34 | /// Storage is not in metadata. 35 | StorageNotFound(&'static str), 36 | /// Storage type does not match requested type. 37 | StorageTypeError, 38 | /// Constant is not in metadata. 39 | ConstantNotFound(&'static str), 40 | /// Variant not found. 41 | VariantIndexNotFound(u8), 42 | /// Api is not in metadata. 43 | RuntimeApiNotFound(String), 44 | /// Exptected a different type of Metadata. Has there been a runtime upgrade inbetween? 45 | MetadataMismatch, 46 | } 47 | 48 | #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Encode, Decode)] 49 | pub enum MetadataConversionError { 50 | InvalidPrefix, 51 | InvalidVersion, 52 | /// Type is missing from type registry. 53 | MissingType(u32), 54 | /// Type was not variant/enum type. 55 | TypeDefNotVariant(u32), 56 | /// Type is not in metadata. 57 | TypeNotFound(u32), 58 | /// Type Name is not in metadata. 59 | TypeNameNotFound(String), 60 | // Path not found. 61 | InvalidTypePath(String), 62 | } 63 | -------------------------------------------------------------------------------- /node-api/src/metadata/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | mod error; 15 | mod from_v14_to_v15; 16 | mod metadata_types; 17 | mod variant_index; 18 | 19 | pub use error::*; 20 | pub use from_v14_to_v15::v14_to_v15; 21 | pub use metadata_types::*; 22 | 23 | #[cfg(feature = "std")] 24 | mod print_metadata; 25 | -------------------------------------------------------------------------------- /node-api/src/metadata/print_metadata.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 Integritee AG and Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | use crate::metadata::{Metadata, PalletMetadata}; 15 | 16 | impl Metadata { 17 | pub fn print_overview(&self) { 18 | let mut string = String::new(); 19 | for pallet in self.pallets() { 20 | string.push_str(pallet.name()); 21 | string.push('\n'); 22 | for storage in pallet.storage() { 23 | string.push_str(" s "); 24 | string.push_str(&storage.name); 25 | string.push('\n'); 26 | } 27 | 28 | if let Some(call_variants) = pallet.call_variants() { 29 | for call in call_variants { 30 | string.push_str(" c "); 31 | string.push_str(&call.name); 32 | string.push('\n'); 33 | } 34 | } 35 | 36 | for constant in pallet.constants() { 37 | string.push_str(" cst "); 38 | string.push_str(&constant.name); 39 | string.push('\n'); 40 | } 41 | 42 | if let Some(events) = pallet.event_variants() { 43 | for event in events { 44 | string.push_str(" e "); 45 | string.push_str(&event.name); 46 | string.push('\n'); 47 | } 48 | } 49 | 50 | if let Some(errors) = pallet.error_variants() { 51 | for error in errors { 52 | string.push_str(" err "); 53 | string.push_str(&error.name); 54 | string.push('\n'); 55 | } 56 | } 57 | } 58 | 59 | println!("{string}"); 60 | } 61 | 62 | pub fn print_pallets(&self) { 63 | for pallet in self.pallets() { 64 | pallet.print() 65 | } 66 | } 67 | 68 | pub fn print_pallets_with_calls(&self) { 69 | for pallet in self.pallets() { 70 | pallet.print_calls(); 71 | } 72 | } 73 | pub fn print_pallets_with_constants(&self) { 74 | for pallet in self.pallets() { 75 | pallet.print_constants(); 76 | } 77 | } 78 | pub fn print_pallet_with_storages(&self) { 79 | for pallet in self.pallets() { 80 | pallet.print_storages(); 81 | } 82 | } 83 | 84 | pub fn print_pallets_with_events(&self) { 85 | for pallet in self.pallets() { 86 | pallet.print_events(); 87 | } 88 | } 89 | 90 | pub fn print_pallets_with_errors(&self) { 91 | for pallet in self.pallets() { 92 | pallet.print_errors(); 93 | } 94 | } 95 | } 96 | 97 | impl PalletMetadata<'_> { 98 | pub fn print(&self) { 99 | println!("----------------- Pallet: '{}' -----------------\n", self.name()); 100 | println!("Pallet id: {}", self.index()); 101 | } 102 | 103 | pub fn print_calls(&self) { 104 | println!("----------------- Calls for Pallet: {} -----------------\n", self.name()); 105 | if let Some(variants) = self.call_variants() { 106 | for variant in variants { 107 | println!("Name: {}, index {}", variant.name, variant.index); 108 | } 109 | }; 110 | println!(); 111 | } 112 | 113 | pub fn print_constants(&self) { 114 | println!("----------------- Constants for Pallet: {} -----------------\n", self.name()); 115 | for constant in self.constants() { 116 | println!("Name: {}, Type {:?}, Value {:?}", constant.name, constant.ty, constant.value); 117 | } 118 | println!(); 119 | } 120 | pub fn print_storages(&self) { 121 | println!("----------------- Storages for Pallet: {} -----------------\n", self.name()); 122 | for storage in self.storage() { 123 | println!( 124 | "Name: {}, Modifier: {:?}, Type {:?}, Default {:?}", 125 | storage.name, storage.modifier, storage.ty, storage.default 126 | ); 127 | } 128 | println!(); 129 | } 130 | 131 | pub fn print_events(&self) { 132 | println!("----------------- Events for Pallet: {} -----------------\n", self.name()); 133 | if let Some(variants) = self.event_variants() { 134 | for variant in variants { 135 | println!("Name: {}", variant.name); 136 | println!("Field: {:?}", variant.fields); 137 | println!("Docs: {:?}", variant.docs); 138 | println!(); 139 | } 140 | }; 141 | println!(); 142 | } 143 | 144 | pub fn print_errors(&self) { 145 | println!("----------------- Errors for Pallet: {} -----------------\n", self.name()); 146 | if let Some(variants) = self.error_variants() { 147 | for variant in variants { 148 | println!("Name: {}", variant.name); 149 | println!("Docs: {:?}", variant.docs); 150 | println!(); 151 | } 152 | }; 153 | println!(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /node-api/src/metadata/variant_index.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 Parity Technologies (UK) Ltd. 2 | // This file is dual-licensed as Apache-2.0 or GPL-3.0. 3 | // see LICENSE for license details. 4 | 5 | //! This file is based on 6 | //! https://github.com/paritytech/subxt/blob/8413c4d2dd625335b9200dc2289670accdf3391a/metadata/src/utils/variant_index.rs 7 | 8 | use alloc::{borrow::ToOwned, collections::BTreeMap, string::String}; 9 | use scale_info::{form::PortableForm, PortableRegistry, TypeDef, Variant}; 10 | 11 | /// Given some type ID and type registry, build a couple of 12 | /// indexes to look up variants by index or name. If the ID provided 13 | /// is not a variant, the index will be empty. 14 | /// 15 | /// API optimized for dealing with the `Option` variant type IDs 16 | /// that we get in metadata pallets. 17 | #[derive(Debug, Clone)] 18 | pub struct VariantIndex { 19 | by_name: BTreeMap, 20 | by_index: BTreeMap, 21 | } 22 | 23 | impl VariantIndex { 24 | /// Build indexes from the optional variant ID. 25 | pub fn build(variant_id: Option, types: &PortableRegistry) -> Self { 26 | let Some(variants) = Self::get(variant_id, types) else { return Self::empty() }; 27 | 28 | let mut by_name = BTreeMap::new(); 29 | let mut by_index = BTreeMap::new(); 30 | for (pos, variant) in variants.iter().enumerate() { 31 | by_name.insert(variant.name.to_owned(), pos); 32 | by_index.insert(variant.index, pos); 33 | } 34 | 35 | Self { by_name, by_index } 36 | } 37 | 38 | /// Build an empty index. 39 | pub fn empty() -> Self { 40 | Self { by_name: Default::default(), by_index: Default::default() } 41 | } 42 | 43 | /// Get the variants we're pointing at; None if this isn't possible. 44 | pub fn get( 45 | variant_id: Option, 46 | types: &PortableRegistry, 47 | ) -> Option<&[Variant]> { 48 | let variant_id = variant_id?; 49 | let TypeDef::Variant(v) = &types.resolve(variant_id)?.type_def else { return None }; 50 | Some(&v.variants) 51 | } 52 | 53 | /// Lookup a variant by name; `None` if the type is not a variant or name isn't found. 54 | pub fn lookup_by_name<'a, K>( 55 | &self, 56 | name: &K, 57 | variant_id: Option, 58 | types: &'a PortableRegistry, 59 | ) -> Option<&'a Variant> 60 | where 61 | String: alloc::borrow::Borrow, 62 | K: core::hash::Hash + Eq + ?Sized + Ord, 63 | { 64 | let pos = *self.by_name.get(name)?; 65 | let variants = Self::get(variant_id, types)?; 66 | variants.get(pos) 67 | } 68 | 69 | /// Lookup a variant by index; `None` if the type is not a variant or index isn't found. 70 | pub fn lookup_by_index<'a>( 71 | &self, 72 | index: u8, 73 | variant_id: Option, 74 | types: &'a PortableRegistry, 75 | ) -> Option<&'a Variant> { 76 | let pos = *self.by_index.get(&index)?; 77 | let variants = Self::get(variant_id, types)?; 78 | variants.get(pos) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-primitives" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/scs/substrate-api-client" 8 | description = "Substrate-api-client primitive types" 9 | readme = "README.md" 10 | categories = ["no-std"] 11 | 12 | 13 | [dependencies] 14 | codec = { workspace = true, features = ['derive'] } 15 | impl-serde = { workspace = true } 16 | primitive-types = { workspace = true, features = ["serde_no_std", "scale-info"] } 17 | scale-info = { workspace = true, features = ["derive"] } 18 | serde = { workspace = true, features = ["derive", "alloc"] } 19 | serde_json = { workspace = true, features = ["alloc"] } 20 | 21 | # substrate no_std 22 | sp-core = { workspace = true, features = ["full_crypto", "serde"] } 23 | sp-crypto-hashing = { workspace = true } 24 | sp-runtime = { workspace = true, features = ["serde"] } 25 | sp-runtime-interface = { workspace = true } 26 | sp-staking = { workspace = true, features = ["serde"] } 27 | sp-version = { workspace = true, features = ["serde"] } 28 | sp-weights = { workspace = true, features = ["serde"] } 29 | # need to add this for the app_crypto macro 30 | sp-application-crypto = { workspace = true, features = ["full_crypto"] } 31 | 32 | # substrate std / wasm only 33 | frame-system = { optional = true, workspace = true } 34 | pallet-assets = { optional = true, workspace = true } 35 | pallet-balances = { optional = true, workspace = true } 36 | pallet-contracts = { optional = true, workspace = true } 37 | pallet-staking = { optional = true, workspace = true } 38 | 39 | [dev-dependencies] 40 | sp-keyring = { workspace = true } 41 | 42 | 43 | [features] 44 | default = ["std"] 45 | # To support `no_std` builds in non-32 bit environments. 46 | disable_target_static_assertions = [ 47 | "sp-runtime-interface/disable_target_static_assertions", 48 | ] 49 | std = [ 50 | "codec/std", 51 | "primitive-types/std", 52 | "scale-info/std", 53 | "serde/std", 54 | "serde_json/std", 55 | # substrate no_std 56 | "sp-core/std", 57 | "sp-crypto-hashing/std", 58 | "sp-runtime/std", 59 | "sp-runtime-interface/std", 60 | "sp-staking/std", 61 | "sp-version/std", 62 | "sp-weights/std", 63 | "sp-application-crypto/std", 64 | # substrate std 65 | "frame-system", 66 | "pallet-assets", 67 | "pallet-balances/std", 68 | ] 69 | staking-xt = ["std", "pallet-staking"] 70 | contracts-xt = ["std", "pallet-contracts"] 71 | -------------------------------------------------------------------------------- /primitives/README.md: -------------------------------------------------------------------------------- 1 | # ac_primitives 2 | 3 | This crate is a submodule of the [substrate-api-client](https://github.com/scs/substrate-api-client). It contains primitives types and helpers used by the api-client and its submodules. 4 | -------------------------------------------------------------------------------- /primitives/src/config/asset_runtime_config.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // This file is dual-licensed as Apache-2.0 or GPL-3.0. 3 | // see LICENSE for license details. 4 | 5 | //! Default set of commonly used types by Substrate and Polkadot nodes that use the asset pallet. 6 | //! 7 | //! This file is mostly subxt. 8 | //! https://github.com/paritytech/subxt/blob/ce0a82e3227efb0eae131f025da5f839d9623e15/subxt/src/config/substrate.rs 9 | 10 | use crate::{ 11 | config::WithExtrinsicParams, AssetTip, Config, DefaultRuntimeConfig, GenericExtrinsicParams, 12 | }; 13 | /// Standard runtime config for Substrate and Polkadot nodes that use the asset pallet. 14 | pub type AssetRuntimeConfig = 15 | WithExtrinsicParams>; 16 | 17 | /// A struct representing the signed extra and additional parameters required 18 | /// to construct a transaction and pay in asset fees. 19 | pub type AssetTipExtrinsicParams = GenericExtrinsicParams::Balance>>; 20 | -------------------------------------------------------------------------------- /primitives/src/config/default_runtime_config.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2022 Parity Technologies (UK) Ltd. 2 | // This file is dual-licensed as Apache-2.0 or GPL-3.0. 3 | // see LICENSE for license details. 4 | 5 | //! Default set of commonly used types by Substrate and Polkadot nodes. 6 | //! 7 | //! This file is mostly subxt. 8 | //! https://github.com/paritytech/subxt/blob/ce0a82e3227efb0eae131f025da5f839d9623e15/subxt/src/config/polkadot.rs 9 | 10 | use crate::{ 11 | config::Config, sr25519, types::AccountData, AccountId32, BlakeTwo256, Block, ExtrinsicSigner, 12 | GenericExtrinsicParams, Header, MultiAddress, MultiSignature, OpaqueExtrinsic, PlainTip, H256, 13 | }; 14 | use codec::{Decode, Encode}; 15 | use core::fmt::Debug; 16 | 17 | /// Standard runtime config for Substrate and Polkadot nodes. 18 | #[derive(Decode, Encode, Clone, Eq, PartialEq, Debug)] 19 | pub struct DefaultRuntimeConfig {} 20 | 21 | impl Config for DefaultRuntimeConfig { 22 | type Index = u32; 23 | type BlockNumber = u32; 24 | type Hash = H256; 25 | type AccountId = AccountId32; 26 | type Address = MultiAddress; 27 | type Signature = MultiSignature; 28 | type Hasher = BlakeTwo256; 29 | type Header = Header; 30 | type AccountData = AccountData; 31 | type ExtrinsicParams = PlainTipExtrinsicParams; 32 | type CryptoKey = sr25519::Pair; 33 | type ExtrinsicSigner = ExtrinsicSigner; 34 | type Block = Block; 35 | type Balance = u128; 36 | type ContractCurrency = u128; 37 | type StakingBalance = u128; 38 | } 39 | 40 | /// A struct representing the signed extra and additional parameters required 41 | /// to construct a transaction and pay in token fees. 42 | pub type PlainTipExtrinsicParams = GenericExtrinsicParams::Balance>>; 43 | -------------------------------------------------------------------------------- /primitives/src/config/rococo_runtime_config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | use crate::{config::WithAddress, AccountId32, DefaultRuntimeConfig, MultiAddress}; 19 | 20 | pub type RococoRuntimeConfig = WithAddress>; 21 | -------------------------------------------------------------------------------- /primitives/src/extrinsics/extrinsic_params_without_hash_check.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Old style Extrinsic Parameters for older Substrate chains that do not yet use the newer transaction Logic. 19 | //! 20 | // E.g in the runtime/src/lib.rs file: 21 | // pub type TxExtension = ( 22 | // frame_system::CheckNonZeroSender, 23 | // frame_system::CheckSpecVersion, 24 | // frame_system::CheckTxVersion, 25 | // frame_system::CheckGenesis, 26 | // frame_system::CheckEra, 27 | // frame_system::CheckNonce, 28 | // frame_system::CheckWeight, 29 | // pallet_transaction_payment::ChargeTransactionPayment, 30 | // ); 31 | 32 | use crate::{ 33 | config::Config, 34 | extrinsic_params::{ExtrinsicParams, GenericAdditionalParams, GenericTxExtension}, 35 | }; 36 | use codec::{Codec, Decode, DecodeWithMemTracking, Encode}; 37 | use scale_info::{StaticTypeInfo, TypeInfo}; 38 | use sp_runtime::{ 39 | generic::Era, 40 | impl_tx_ext_default, 41 | traits::{Dispatchable, TransactionExtension}, 42 | }; 43 | 44 | #[derive(Decode, Encode, Copy, Clone, Eq, PartialEq, Debug, TypeInfo)] 45 | pub struct TxExtensionWithoutHashCheck { 46 | pub era: Era, 47 | #[codec(compact)] 48 | pub nonce: Index, 49 | pub tip: Tip, 50 | } 51 | 52 | impl TxExtensionWithoutHashCheck { 53 | pub fn new(era: Era, nonce: Index, tip: Tip) -> Self { 54 | { 55 | Self { era, nonce, tip } 56 | } 57 | } 58 | } 59 | 60 | impl TransactionExtension for TxExtensionWithoutHashCheck 61 | where 62 | Call: Dispatchable, 63 | TxExtensionWithoutHashCheck: Codec 64 | + core::fmt::Debug 65 | + Sync 66 | + Send 67 | + Clone 68 | + Eq 69 | + PartialEq 70 | + StaticTypeInfo 71 | + DecodeWithMemTracking, 72 | Tip: Codec 73 | + core::fmt::Debug 74 | + Sync 75 | + Send 76 | + Clone 77 | + Eq 78 | + PartialEq 79 | + StaticTypeInfo 80 | + DecodeWithMemTracking, 81 | Index: Codec 82 | + core::fmt::Debug 83 | + Sync 84 | + Send 85 | + Clone 86 | + Eq 87 | + PartialEq 88 | + StaticTypeInfo 89 | + DecodeWithMemTracking, 90 | { 91 | const IDENTIFIER: &'static str = "TxExtensionWithoutHashCheck"; 92 | type Implicit = (); 93 | type Pre = (); 94 | type Val = (); 95 | 96 | impl_tx_ext_default!(Call; weight validate prepare); 97 | } 98 | 99 | pub type ImplicitWithoutHashCheck = ((), u32, u32, Hash, Hash, (), (), ()); 100 | 101 | /// An implementation of [`ExtrinsicParams`] that is suitable for constructing 102 | /// extrinsics that can be sent to a node with the same signed extra and additional 103 | /// parameters as a Polkadot/Substrate node. 104 | #[derive(Decode, Encode, Clone, Eq, PartialEq, Debug)] 105 | pub struct ExtrinsicParamsWithoutHashCheck { 106 | era: Era, 107 | nonce: T::Index, 108 | tip: Tip, 109 | spec_version: u32, 110 | transaction_version: u32, 111 | genesis_hash: T::Hash, 112 | mortality_checkpoint: T::Hash, 113 | } 114 | 115 | impl ExtrinsicParams for ExtrinsicParamsWithoutHashCheck 116 | where 117 | T: Config, 118 | u128: From, 119 | Tip: Copy + Default + Encode, 120 | { 121 | type AdditionalParams = GenericAdditionalParams; 122 | type TxExtension = GenericTxExtension; 123 | type Implicit = ImplicitWithoutHashCheck; 124 | 125 | fn new( 126 | spec_version: u32, 127 | transaction_version: u32, 128 | nonce: T::Index, 129 | genesis_hash: T::Hash, 130 | additional_params: Self::AdditionalParams, 131 | ) -> Self { 132 | Self { 133 | era: additional_params.era, 134 | tip: additional_params.tip, 135 | spec_version, 136 | transaction_version, 137 | genesis_hash, 138 | mortality_checkpoint: additional_params.mortality_checkpoint.unwrap_or(genesis_hash), 139 | nonce, 140 | } 141 | } 142 | 143 | fn signed_extra(&self) -> Self::TxExtension { 144 | self.transaction_extension() 145 | } 146 | 147 | fn transaction_extension(&self) -> Self::TxExtension { 148 | Self::TxExtension::new(self.era, self.nonce, self.tip) 149 | } 150 | 151 | fn additional_signed(&self) -> Self::Implicit { 152 | self.implicit() 153 | } 154 | 155 | fn implicit(&self) -> Self::Implicit { 156 | { 157 | ( 158 | (), 159 | self.spec_version, 160 | self.transaction_version, 161 | self.genesis_hash, 162 | self.mortality_checkpoint, 163 | (), 164 | (), 165 | (), 166 | ) 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /primitives/src/extrinsics/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | //! Primitives for substrate extrinsics. 19 | 20 | pub use extrinsic_params::{ 21 | AssetTip, ExtrinsicParams, GenericAdditionalParams, GenericExtrinsicParams, GenericImplicit, 22 | GenericTxExtension, PlainTip, SignedPayload, 23 | }; 24 | #[allow(deprecated)] 25 | pub use extrinsic_v4::deprecated; 26 | pub use signer::{ExtrinsicSigner, SignExtrinsic}; 27 | pub use sp_runtime::generic::{Preamble, UncheckedExtrinsic}; 28 | 29 | /// Call Index used a prefix of every extrinsic call. 30 | pub type CallIndex = [u8; 2]; 31 | 32 | pub mod extrinsic_params; 33 | pub mod extrinsic_params_without_hash_check; 34 | mod extrinsic_v4; 35 | pub mod signer; 36 | -------------------------------------------------------------------------------- /primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | #![cfg_attr(not(feature = "std"), no_std)] 19 | 20 | extern crate alloc; 21 | 22 | // Re-export everything. 23 | pub use config::*; 24 | pub use extrinsics::*; 25 | pub use rpc_numbers::*; 26 | pub use rpc_params::*; 27 | pub use types::*; 28 | 29 | pub mod config; 30 | pub mod extrinsics; 31 | pub mod rpc_numbers; 32 | pub mod rpc_params; 33 | pub mod types; 34 | 35 | // Re-export substrate types for easy import on user side. 36 | pub use sp_core::{ 37 | sr25519, 38 | storage::{StorageChangeSet, StorageData, StorageKey}, 39 | Bytes, H256, 40 | }; 41 | pub use sp_runtime::{ 42 | generic::{Block, Digest, DigestItem, Header, SignedBlock}, 43 | traits::{BlakeTwo256, Block as BlockTrait, Hash as HashTrait, Header as HeaderTrait}, 44 | AccountId32, ConsensusEngineId, Justifications, MultiAddress, MultiSignature, OpaqueExtrinsic, 45 | }; 46 | pub use sp_version::RuntimeVersion; 47 | pub use sp_weights::Weight; 48 | -------------------------------------------------------------------------------- /primitives/src/rpc_numbers.rs: -------------------------------------------------------------------------------- 1 | // This file is part of Substrate. 2 | 3 | // Copyright (C) 2017-2022 Parity Technologies (UK) Ltd. 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | //! A number type that can be serialized both as a number or a string that encodes a number in a 19 | //! string. 20 | 21 | // Copied the whole file from substrate, as sp_rpc is not no_std compatible. 22 | // https://github.com/paritytech/substrate/blob/cd2fdcf85eb96c53ce2a5d418d4338eb92f5d4f5/primitives/rpc/src/number.rs 23 | 24 | use core::fmt::Debug; 25 | use primitive_types::U256; 26 | use serde::{Deserialize, Serialize}; 27 | 28 | /// A number type that can be serialized both as a number or a string that encodes a number in a 29 | /// string. 30 | /// 31 | /// We allow two representations of the block number as input. Either we deserialize to the type 32 | /// that is specified in the block type or we attempt to parse given hex value. 33 | /// 34 | /// The primary motivation for having this type is to avoid overflows when using big integers in 35 | /// JavaScript (which we consider as an important RPC API consumer). 36 | #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] 37 | #[serde(untagged)] 38 | pub enum NumberOrHex { 39 | /// The number represented directly. 40 | Number(u64), 41 | /// Hex representation of the number. 42 | Hex(U256), 43 | } 44 | 45 | impl Default for NumberOrHex { 46 | fn default() -> Self { 47 | Self::Number(Default::default()) 48 | } 49 | } 50 | 51 | impl NumberOrHex { 52 | /// Converts this number into an U256. 53 | pub fn into_u256(self) -> U256 { 54 | match self { 55 | NumberOrHex::Number(n) => n.into(), 56 | NumberOrHex::Hex(h) => h, 57 | } 58 | } 59 | } 60 | 61 | impl From for NumberOrHex { 62 | fn from(n: u32) -> Self { 63 | NumberOrHex::Number(n.into()) 64 | } 65 | } 66 | 67 | impl From for NumberOrHex { 68 | fn from(n: u64) -> Self { 69 | NumberOrHex::Number(n) 70 | } 71 | } 72 | 73 | impl From for NumberOrHex { 74 | fn from(n: u128) -> Self { 75 | NumberOrHex::Hex(n.into()) 76 | } 77 | } 78 | 79 | impl From for NumberOrHex { 80 | fn from(n: U256) -> Self { 81 | NumberOrHex::Hex(n) 82 | } 83 | } 84 | 85 | /// An error type that signals an out-of-range conversion attempt. 86 | pub struct TryFromIntError(pub(crate) ()); 87 | 88 | impl TryFrom for u32 { 89 | type Error = TryFromIntError; 90 | fn try_from(num_or_hex: NumberOrHex) -> Result { 91 | num_or_hex.into_u256().try_into().map_err(|_| TryFromIntError(())) 92 | } 93 | } 94 | 95 | impl TryFrom for u64 { 96 | type Error = TryFromIntError; 97 | fn try_from(num_or_hex: NumberOrHex) -> Result { 98 | num_or_hex.into_u256().try_into().map_err(|_| TryFromIntError(())) 99 | } 100 | } 101 | 102 | impl TryFrom for u128 { 103 | type Error = TryFromIntError; 104 | fn try_from(num_or_hex: NumberOrHex) -> Result { 105 | num_or_hex.into_u256().try_into().map_err(|_| TryFromIntError(())) 106 | } 107 | } 108 | 109 | impl From for U256 { 110 | fn from(num_or_hex: NumberOrHex) -> U256 { 111 | num_or_hex.into_u256() 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-05-06" 3 | targets = ["wasm32-unknown-unknown", "wasm32-wasip1"] 4 | profile = "default" # include rustfmt, clippy 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Basic 2 | edition = "2021" 3 | hard_tabs = true 4 | max_width = 100 5 | use_small_heuristics = "Max" 6 | # Imports 7 | imports_granularity = "Crate" 8 | reorder_imports = true 9 | # Consistency 10 | newline_style = "Unix" 11 | # Misc 12 | chain_width = 80 13 | spaces_around_ranges = false 14 | match_arm_leading_pipes = "Preserve" 15 | match_arm_blocks = false 16 | match_block_trailing_comma = true 17 | trailing_comma = "Vertical" 18 | trailing_semicolon = false 19 | use_field_init_shorthand = true -------------------------------------------------------------------------------- /test-no-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test-no-std" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | 8 | [dependencies] 9 | libc = { version = "0.2", default-features = false } 10 | 11 | # local dependencies 12 | ac-compose-macros = { workspace = true, optional = true, features = ["disable_target_static_assertions", "sync-api"] } 13 | ac-node-api = { workspace = true, optional = true, features = ["disable_target_static_assertions"] } 14 | ac-primitives = { workspace = true, optional = true, features = ["disable_target_static_assertions"] } 15 | substrate-api-client = { workspace = true, optional = true, features = ["disable_target_static_assertions", "sync-api"] } 16 | 17 | # substrate dependencies 18 | sp-io = { workspace = true, features = ["disable_oom", "disable_panic_handler"] } 19 | 20 | [features] 21 | # It is better to test the no-std crates standalone (don't enable both features at the same time) because dependency 22 | # leaks might cause successful `no-std` builds, which would fail in standalone build. 23 | api-client = ["substrate-api-client"] 24 | compose-macros = ["ac-compose-macros"] 25 | node-api = ["ac-node-api"] 26 | primitives = ["ac-primitives"] 27 | -------------------------------------------------------------------------------- /test-no-std/README.md: -------------------------------------------------------------------------------- 1 | This crate serves the purpose of testing no_std support of substrate-api-client 2 | 3 | Just build it with 4 | ``` 5 | cargo build 6 | ``` 7 | 8 | If you don't see errors about duplicate std, it's fine. -------------------------------------------------------------------------------- /test-no-std/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(libc, lang_items)] 2 | #![feature(alloc_error_handler)] 3 | #![no_std] 4 | #![no_main] 5 | 6 | #[cfg(not(any( 7 | feature = "api-client", 8 | feature = "node-api", 9 | feature = "compose-macros", 10 | feature = "primitives" 11 | )))] 12 | compile_error!( 13 | "either feature \"api-client\", \"compose-macro\", or feature \"node-api\" must be enabled" 14 | ); 15 | 16 | // DUTs 17 | 18 | #[cfg(feature = "api-client")] 19 | extern crate substrate_api_client; 20 | 21 | #[cfg(feature = "compose-macros")] 22 | extern crate ac_compose_macros; 23 | 24 | #[cfg(feature = "node-api")] 25 | extern crate ac_node_api; 26 | 27 | #[cfg(feature = "primitives")] 28 | extern crate ac_primitives; 29 | 30 | // The libc crate allows importing functions from C. 31 | extern crate libc; 32 | use core::{ 33 | alloc::{GlobalAlloc, Layout}, 34 | cell::UnsafeCell, 35 | panic::PanicInfo, 36 | ptr::null_mut, 37 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, 38 | }; 39 | 40 | // A list of C functions that are being imported 41 | extern "C" { 42 | pub fn printf(format: *const u8, ...) -> i32; 43 | } 44 | 45 | #[no_mangle] 46 | // The main function, with its input arguments ignored, and an exit status is returned 47 | pub extern "C" fn main(_nargs: i32, _args: *const *const u8) -> i32 { 48 | // Print "Hello, World" to stdout using printf 49 | unsafe { 50 | printf(b"Hello, World!\n" as *const u8); 51 | } 52 | 53 | // Exit with a return status of 0. 54 | 0 55 | } 56 | 57 | #[lang = "eh_personality"] 58 | extern "C" fn eh_personality() {} 59 | 60 | #[panic_handler] 61 | fn panic(_panic: &PanicInfo<'_>) -> ! { 62 | loop {} 63 | } 64 | 65 | #[alloc_error_handler] 66 | fn foo(_: core::alloc::Layout) -> ! { 67 | extern "C" { 68 | fn abort() -> !; 69 | } 70 | unsafe { abort() } 71 | } 72 | 73 | const ARENA_SIZE: usize = 128 * 1024; 74 | const MAX_SUPPORTED_ALIGN: usize = 4096; 75 | #[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN 76 | struct SimpleAllocator { 77 | arena: UnsafeCell<[u8; ARENA_SIZE]>, 78 | remaining: AtomicUsize, // we allocate from the top, counting down 79 | } 80 | 81 | #[global_allocator] 82 | static ALLOCATOR: SimpleAllocator = SimpleAllocator { 83 | arena: UnsafeCell::new([0x55; ARENA_SIZE]), 84 | remaining: AtomicUsize::new(ARENA_SIZE), 85 | }; 86 | unsafe impl Sync for SimpleAllocator {} 87 | 88 | unsafe impl GlobalAlloc for SimpleAllocator { 89 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 90 | let size = layout.size(); 91 | let align = layout.align(); 92 | 93 | // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2. 94 | // So we can safely use a mask to ensure alignment without worrying about UB. 95 | let align_mask_to_round_down = !(align - 1); 96 | 97 | if align > MAX_SUPPORTED_ALIGN { 98 | return null_mut(); 99 | } 100 | 101 | let mut allocated = 0; 102 | if self 103 | .remaining 104 | .fetch_update(SeqCst, SeqCst, |mut remaining| { 105 | if size > remaining { 106 | return None; 107 | } 108 | remaining -= size; 109 | remaining &= align_mask_to_round_down; 110 | allocated = remaining; 111 | Some(remaining) 112 | }) 113 | .is_err() 114 | { 115 | return null_mut(); 116 | }; 117 | (self.arena.get() as *mut u8).add(allocated) 118 | } 119 | unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} 120 | } 121 | -------------------------------------------------------------------------------- /testing/async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-testing-async" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | 8 | [dev-dependencies] 9 | codec = { workspace = true, features = ["std"] } 10 | tokio = { workspace = true } 11 | jsonrpsee = { workspace = true, features = ["server"] } 12 | 13 | 14 | # Substrate dependencies 15 | frame-support = { workspace = true, features = ["std"] } 16 | rococo-runtime = { workspace = true, features = ["std"] } 17 | sp-core = { workspace = true, features = ["std"] } 18 | sp-crypto-hashing = { workspace = true, features = ["std"] } 19 | sp-keyring = { workspace = true, features = ["std"] } 20 | sp-runtime = { workspace = true, features = ["std"] } 21 | pallet-balances = { workspace = true, features = ["std"] } 22 | pallet-society = { workspace = true, features = ["std"] } 23 | 24 | # local deps 25 | substrate-api-client = { workspace = true, features = ["std", "staking-xt", "contracts-xt", "jsonrpsee-client"] } 26 | -------------------------------------------------------------------------------- /testing/async/examples/chain_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the chain rpc interface functions, including testing the RococoRuntimeConfig 17 | //! and Signer generation for the RococoRuntimeConfig. 18 | 19 | use sp_keyring::Sr25519Keyring; 20 | use substrate_api_client::{ 21 | ac_primitives::RococoRuntimeConfig, 22 | rpc::{HandleSubscription, JsonrpseeClient}, 23 | Api, GetChainInfo, SubscribeChain, 24 | }; 25 | 26 | #[tokio::main] 27 | async fn main() { 28 | // Setup 29 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 30 | let mut api = Api::::new(client).await.unwrap(); 31 | let signer = Sr25519Keyring::Alice.pair(); 32 | api.set_signer(signer.into()); 33 | 34 | // GetChainInfo 35 | let finalized_header_hash = api.get_finalized_head().await.unwrap().unwrap(); 36 | let _latest_header = api.get_header(None).await.unwrap().unwrap(); 37 | let _some_header = api.get_header(Some(finalized_header_hash)).await.unwrap().unwrap(); 38 | let _block_hash = api.get_block_hash(None).await.unwrap().unwrap(); 39 | let block_hash = api.get_block_hash(Some(1)).await.unwrap().unwrap(); 40 | let _block = api.get_block(None).await.unwrap().unwrap(); 41 | let _block = api.get_block(Some(block_hash)).await.unwrap().unwrap(); 42 | let _block = api.get_block_by_num(None).await.unwrap().unwrap(); 43 | let _block = api.get_block_by_num(Some(2)).await.unwrap().unwrap(); 44 | let _signed_block = api.get_signed_block(None).await.unwrap().unwrap(); 45 | let _signed_block = api.get_signed_block(Some(block_hash)).await.unwrap().unwrap(); 46 | let _signed_block = api.get_signed_block_by_num(None).await.unwrap().unwrap(); 47 | let _signed_block = api.get_signed_block_by_num(Some(1)).await.unwrap().unwrap(); 48 | 49 | // Subscription 50 | let mut finalized_head_subscription = api.subscribe_finalized_heads().await.unwrap(); 51 | let _some_head = finalized_head_subscription.next().await.unwrap().unwrap(); 52 | } 53 | -------------------------------------------------------------------------------- /testing/async/examples/dispatch_errors_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the dispatch error. 17 | 18 | use sp_core::H256; 19 | use sp_keyring::Sr25519Keyring; 20 | use sp_runtime::MultiAddress; 21 | use substrate_api_client::{ 22 | ac_primitives::RococoRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api, 23 | Error, GetAccountInformation, GetBalance, SubmitAndWatch, XtStatus, 24 | }; 25 | 26 | #[tokio::main] 27 | async fn main() { 28 | // Setup 29 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 30 | let alice_signer = Sr25519Keyring::Alice.pair(); 31 | let bob_signer = Sr25519Keyring::Bob.pair(); 32 | let mut api = Api::::new(client).await.unwrap(); 33 | 34 | let alice = Sr25519Keyring::Alice.to_account_id(); 35 | let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free; 36 | println!("[+] Alice's Free Balance is {}\n", balance_of_alice); 37 | 38 | let bob = Sr25519Keyring::Bob.to_account_id(); 39 | let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free; 40 | println!("[+] Bob's Free Balance is {}\n", balance_of_bob); 41 | 42 | let one = Sr25519Keyring::One.to_account_id(); 43 | let balance_of_one = api.get_account_data(&one).await.unwrap().unwrap_or_default().free; 44 | println!("[+] One's Free Balance is {}\n", balance_of_one); 45 | 46 | //BadOrigin 47 | api.set_signer(bob_signer.into()); 48 | //Can only be called by root 49 | let xt = api 50 | .balance_force_set_balance(MultiAddress::Id(alice.clone()), 10) 51 | .await 52 | .unwrap(); 53 | 54 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).await; 55 | match result { 56 | Err(Error::FailedExtrinsic(extrinsic_error)) => { 57 | let dispatch_error = extrinsic_error.dispatch_error(); 58 | let report = extrinsic_error.get_report::().unwrap(); 59 | assert!(report.block_hash.is_some()); 60 | assert!(report.events.is_some()); 61 | assert!(format!("{dispatch_error:?}").contains("BadOrigin")); 62 | println!("[+] BadOrigin error: Bob can't force set balance"); 63 | }, 64 | _ => panic!("Expected Failed Extrinisc Error"), 65 | } 66 | 67 | //BelowMinimum 68 | api.set_signer(alice_signer.into()); 69 | let xt = api 70 | .balance_transfer_allow_death(MultiAddress::Id(one.clone()), 999999) 71 | .await 72 | .unwrap(); 73 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).await; 74 | match result { 75 | Err(Error::FailedExtrinsic(extrinsic_error)) => { 76 | let dispatch_error = extrinsic_error.dispatch_error(); 77 | let report = extrinsic_error.get_report::().unwrap(); 78 | assert!(report.block_hash.is_some()); 79 | assert!(format!("{dispatch_error:?}").contains("BelowMinimum")); 80 | }, 81 | _ => panic!("Expected Failed Extrinisc Error"), 82 | } 83 | let existential_deposit = api.get_existential_deposit().await.unwrap(); 84 | println!( 85 | "[+] BelowMinimum error: balance (999999) is below the existential deposit ({})", 86 | &existential_deposit 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /testing/async/examples/dump_metadata.rs: -------------------------------------------------------------------------------- 1 | use sp_core::Bytes; 2 | use std::{fs::File, io::prelude::*}; 3 | use substrate_api_client::{ 4 | ac_compose_macros::rpc_params, 5 | rpc::{JsonrpseeClient, Request}, 6 | }; 7 | 8 | // Simple file to dump new metdata as a file. 9 | // Run with: cargo run -p ac-testing --example dump_metadata 10 | 11 | #[tokio::main] 12 | async fn main() { 13 | let client = JsonrpseeClient::new_with_port("wss://kusama-rpc.polkadot.io", 443) 14 | .await 15 | .unwrap(); 16 | let metadata_bytes: Bytes = client.request("state_getMetadata", rpc_params![]).await.unwrap(); 17 | let mut file = File::create("new_ksm_metadata.bin").unwrap(); 18 | file.write_all(&metadata_bytes.0).unwrap(); 19 | } 20 | -------------------------------------------------------------------------------- /testing/async/examples/events_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the frame system interface functions. 17 | 18 | use codec::Decode; 19 | use frame_support::dispatch::DispatchInfo; 20 | use rococo_runtime::RuntimeEvent; 21 | use sp_keyring::Sr25519Keyring; 22 | use substrate_api_client::{ 23 | ac_node_api::{EventDetails, StaticEvent}, 24 | ac_primitives::{Config, RococoRuntimeConfig}, 25 | extrinsic::BalancesExtrinsics, 26 | rpc::JsonrpseeClient, 27 | Api, FetchEvents, GetChainInfo, SubmitAndWatch, SubscribeEvents, XtStatus, 28 | }; 29 | 30 | type Hash = ::Hash; 31 | 32 | /// Check out frame_system::Event::ExtrinsicSuccess: 33 | #[derive(Decode, Debug)] 34 | struct ExtrinsicSuccess { 35 | _dispatch_info: DispatchInfo, 36 | } 37 | 38 | impl StaticEvent for ExtrinsicSuccess { 39 | const PALLET: &'static str = "System"; 40 | const EVENT: &'static str = "ExtrinsicSuccess"; 41 | } 42 | 43 | #[tokio::main] 44 | async fn main() { 45 | // Setup 46 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 47 | let alice_pair = Sr25519Keyring::Alice.pair(); 48 | let mut api = Api::::new(client).await.unwrap(); 49 | api.set_signer(alice_pair.into()); 50 | 51 | let bob = Sr25519Keyring::Bob.to_account_id(); 52 | 53 | // Test `fetch_events_from_block`: There should always be at least the 54 | // timestamp set event. 55 | let block_hash = api.get_block_hash(None).await.unwrap().unwrap(); 56 | let events = api.fetch_events_from_block(block_hash).await.unwrap(); 57 | assert!(!events.is_empty()); 58 | println!("{events:?}"); 59 | 60 | // Submit a test-extrinsic to test `fetch_events_for_extrinsic`. 61 | let xt = api.balance_transfer_allow_death(bob.into(), 1000).await.unwrap(); 62 | let report = api 63 | .submit_and_watch_extrinsic_until_without_events(xt, XtStatus::InBlock) 64 | .await 65 | .unwrap(); 66 | 67 | let extrinsic_events = api 68 | .fetch_events_for_extrinsic(report.extrinsic_hash, report.block_hash.unwrap()) 69 | .await 70 | .unwrap(); 71 | assert_associated_events_match_expected(extrinsic_events); 72 | 73 | // Subscribe to system events. 74 | let mut event_subscription = api.subscribe_events().await.unwrap(); 75 | 76 | // Wait for event callbacks from the node, which are received via subscription. 77 | for _ in 0..5 { 78 | let event_records = event_subscription 79 | .next_events::::Hash>() 80 | .await 81 | .unwrap() 82 | .unwrap(); 83 | for event_record in &event_records { 84 | println!("got event: {:?} {:?}", event_record.phase, event_record.event); 85 | match &event_record.event { 86 | RuntimeEvent::System(_) => println!("Got System event, all good"), 87 | _ => panic!("Unexpected event"), 88 | } 89 | } 90 | } 91 | 92 | // Wait for event callbacks from the node, which are received via subscription, in case no RuntimeEvents are accessible. 93 | for _ in 0..5 { 94 | let events = event_subscription.next_events_from_metadata().await.unwrap().unwrap(); 95 | for event in events.iter() { 96 | let event = event.unwrap(); 97 | println!("got event: {:?} {:?}", event.pallet_name(), event.variant_name()); 98 | if let Ok(Some(_extrinisic_success)) = event.as_event::() { 99 | println!("Got System event, all good"); 100 | } else { 101 | panic!("Unexpected event"); 102 | } 103 | } 104 | } 105 | } 106 | 107 | fn assert_associated_events_match_expected(events: Vec>) { 108 | // First event 109 | assert_eq!(events[0].pallet_name(), "Balances"); 110 | assert_eq!(events[0].variant_name(), "Withdraw"); 111 | 112 | assert_eq!(events[1].pallet_name(), "Balances"); 113 | assert_eq!(events[1].variant_name(), "Transfer"); 114 | 115 | assert_eq!(events[2].pallet_name(), "Balances"); 116 | assert_eq!(events[2].variant_name(), "Deposit"); 117 | 118 | assert_eq!(events[3].pallet_name(), "Treasury"); 119 | assert_eq!(events[3].variant_name(), "Deposit"); 120 | 121 | assert_eq!(events[4].pallet_name(), "Balances"); 122 | assert_eq!(events[4].variant_name(), "Deposit"); 123 | 124 | assert_eq!(events[5].pallet_name(), "TransactionPayment"); 125 | assert_eq!(events[5].variant_name(), "TransactionFeePaid"); 126 | 127 | assert_eq!(events[6].pallet_name(), "System"); 128 | assert_eq!(events[6].variant_name(), "ExtrinsicSuccess"); 129 | } 130 | -------------------------------------------------------------------------------- /testing/async/examples/frame_system_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the frame system interface functions. 17 | 18 | use codec::Decode; 19 | use frame_support::dispatch::DispatchInfo; 20 | use sp_keyring::Sr25519Keyring; 21 | use substrate_api_client::{ 22 | ac_node_api::StaticEvent, ac_primitives::RococoRuntimeConfig, rpc::JsonrpseeClient, Api, 23 | GetAccountInformation, SystemApi, 24 | }; 25 | 26 | /// Check out frame_system::Event::ExtrinsicSuccess: 27 | #[derive(Decode, Debug)] 28 | struct ExtrinsicSuccess { 29 | _dispatch_info: DispatchInfo, 30 | } 31 | 32 | impl StaticEvent for ExtrinsicSuccess { 33 | const PALLET: &'static str = "System"; 34 | const EVENT: &'static str = "ExtrinsicSuccess"; 35 | } 36 | 37 | #[tokio::main] 38 | async fn main() { 39 | // Setup 40 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 41 | let alice_pair = Sr25519Keyring::Alice.pair(); 42 | let mut api = Api::::new(client).await.unwrap(); 43 | api.set_signer(alice_pair.into()); 44 | 45 | let alice = Sr25519Keyring::Alice.to_account_id(); 46 | 47 | // GetAccountInformation 48 | let _account_info = api.get_account_info(&alice).await.unwrap().unwrap(); 49 | let _account_data = api.get_account_data(&alice).await.unwrap().unwrap(); 50 | 51 | // Empty account information 52 | let inexistent_account = Sr25519Keyring::Two.to_account_id(); 53 | let maybe_account_info = api.get_account_info(&inexistent_account).await.unwrap(); 54 | assert!(maybe_account_info.is_none()); 55 | let maybe_account_data = api.get_account_data(&inexistent_account).await.unwrap(); 56 | assert!(maybe_account_data.is_none()); 57 | 58 | // System Api 59 | let next_index = api.get_system_account_next_index(alice).await.unwrap(); 60 | // Alice has not yet sent any extrinsic, so next_index should be 0. 61 | assert_eq!(next_index, 0); 62 | 63 | let system_name = api.get_system_name().await.unwrap(); 64 | println!("System name: {system_name}"); 65 | 66 | let system_version = api.get_system_version().await.unwrap(); 67 | println!("System version: {system_version}"); 68 | 69 | let system_chain = api.get_system_chain().await.unwrap(); 70 | println!("System chain: {system_chain}"); 71 | 72 | let system_chain_type = api.get_system_chain_type().await.unwrap(); 73 | println!("System chain type: {system_chain_type:?}"); 74 | 75 | let system_properties = api.get_system_properties().await.unwrap(); 76 | println!("System properties: {system_properties:?}"); 77 | 78 | let system_health = api.get_system_health().await.unwrap(); 79 | println!("System health: {system_health}"); 80 | 81 | let system_local_peer_id = api.get_system_local_peer_id().await.unwrap(); 82 | println!("System local peer id: {system_local_peer_id:?}"); 83 | 84 | let system_local_listen_addresses = api.get_system_local_listen_addresses().await.unwrap(); 85 | println!("System local listen addresses: {system_local_listen_addresses:?}"); 86 | } 87 | -------------------------------------------------------------------------------- /testing/async/examples/jsonrpsee_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the Jsonrpseeclient Wrapper. Should happen during runtime, otherwise no connection can be etablished. 17 | 18 | use core::panic; 19 | 20 | use jsonrpsee::{ 21 | client_transport::ws::{Url, WsTransportClientBuilder}, 22 | core::client::{Client, ClientBuilder}, 23 | server::{RpcModule, Server}, 24 | }; 25 | use substrate_api_client::rpc::JsonrpseeClient; 26 | use tokio::{sync::oneshot, task, time, time::Duration}; 27 | 28 | #[tokio::main] 29 | async fn main() { 30 | let port = 9944; 31 | let address = "ws://127.0.0.1"; 32 | 33 | let client = JsonrpseeClient::with_default_url().await; 34 | let client2 = JsonrpseeClient::new_with_port(address, port).await; 35 | let client3 = JsonrpseeClient::new_with_port(address, 9994).await; 36 | 37 | assert!(client.is_ok()); 38 | assert!(client2.is_ok()); 39 | // No node running at this port - creation should fail. 40 | assert!(client3.is_err()); 41 | 42 | // Test new_with_client and extra functionality of inner Jsonrpseee client. 43 | let (tx, mut rx) = oneshot::channel(); 44 | let finish_message = "Finishing task"; 45 | 46 | // Start server. 47 | let server = Server::builder().build("127.0.0.1:0").await.unwrap(); 48 | let addr = server.local_addr().unwrap(); 49 | let server_handle = server.start(RpcModule::new(())); 50 | 51 | // Create client and connect. 52 | let uri = Url::parse(&format!("ws://{}", addr)).unwrap(); 53 | let (tx1, rx1) = WsTransportClientBuilder::default().build(uri).await.unwrap(); 54 | let client: Client = ClientBuilder::default().build_with_tokio(tx1, rx1); 55 | let api_rpsee_client = JsonrpseeClient::new_with_client(client); 56 | assert!(api_rpsee_client.is_connected()); 57 | 58 | let client_handle = task::spawn(async move { 59 | println!("Waiting for client disconnect"); 60 | api_rpsee_client.on_disconnect().await; 61 | time::sleep(Duration::from_secs(2)).await; 62 | println!("Disconnected due to: {:?}", api_rpsee_client.disconnect_reason().await); 63 | tx.send(finish_message).unwrap(); 64 | }); 65 | 66 | // Drop server such that client gets a disconnect. 67 | drop(server_handle); 68 | 69 | // Wait for the disconnect message. 70 | let timeout = 5; 71 | let mut ctr = 0; 72 | loop { 73 | if let Ok(message) = rx.try_recv() { 74 | assert_eq!(finish_message, message); 75 | println!("{message}"); 76 | break; 77 | } else { 78 | ctr += 1; 79 | if ctr == timeout { 80 | panic!("Timeout"); 81 | } 82 | time::sleep(Duration::from_secs(1)).await; 83 | println!("sleeping.."); 84 | } 85 | } 86 | 87 | client_handle.await.unwrap(); 88 | } 89 | -------------------------------------------------------------------------------- /testing/async/examples/pallet_balances_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the pallet balances interface functions. 17 | 18 | use codec::Encode; 19 | use sp_keyring::Sr25519Keyring; 20 | use substrate_api_client::{ 21 | ac_primitives::RococoRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api, 22 | GetAccountInformation, GetBalance, GetTransactionPayment, SubmitAndWatch, XtStatus, 23 | }; 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | // Setup 28 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 29 | let mut api = Api::::new(client).await.unwrap(); 30 | 31 | let ed = api.get_existential_deposit().await.unwrap(); 32 | println!("[+] Existential deposit is {}\n", ed); 33 | 34 | let alice = Sr25519Keyring::Alice.to_account_id(); 35 | let alice_signer = Sr25519Keyring::Alice.pair(); 36 | api.set_signer(alice_signer.into()); 37 | let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free; 38 | println!("[+] Alice's Free Balance is {}\n", balance_of_alice); 39 | 40 | let bob = Sr25519Keyring::Bob.to_account_id(); 41 | let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free; 42 | println!("[+] Bob's Free Balance is {}\n", balance_of_bob); 43 | 44 | // Rough estimate of fees for three transactions 45 | let dummy_xt = api 46 | .balance_transfer_keep_alive(bob.clone().into(), balance_of_alice) 47 | .await 48 | .unwrap() 49 | .encode(); 50 | let transaction_fee = 51 | api.get_fee_details(&dummy_xt.into(), None).await.unwrap().unwrap().final_fee(); 52 | println!("[+] Transaction Fee is {}\n", transaction_fee); 53 | 54 | let xt = api 55 | .balance_transfer_keep_alive( 56 | bob.clone().into(), 57 | balance_of_alice / 2 - (3 * transaction_fee), 58 | ) 59 | .await 60 | .unwrap(); 61 | let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await; 62 | // This call should succeed as alice has enough money 63 | assert!(report.is_ok()); 64 | 65 | // Alice now has half of her balance plus two transaction fees left 66 | // (one has been deducted by the transaction above). 67 | let estimated_balance_of_alice = balance_of_alice / 2 + 2 * transaction_fee; 68 | 69 | let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free; 70 | println!("[+] Alice's Free Balance is {}\n", balance_of_alice); 71 | assert_eq!(balance_of_alice, estimated_balance_of_alice); 72 | 73 | let xt = api 74 | .balance_transfer_keep_alive(bob.clone().into(), balance_of_alice - transaction_fee - 1) 75 | .await 76 | .unwrap(); 77 | 78 | let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await; 79 | // This call should fail as alice would fall below the existential deposit 80 | assert!(report.is_err()); 81 | 82 | let balance_of_alice = api.get_account_data(&alice).await.unwrap().unwrap().free; 83 | println!("[+] Alice's Free Balance is {}\n", balance_of_alice); 84 | 85 | let dummy_xt = api 86 | .balance_transfer_allow_death(bob.clone().into(), balance_of_alice) 87 | .await 88 | .unwrap() 89 | .encode(); 90 | let transaction_fee = 91 | api.get_fee_details(&dummy_xt.into(), None).await.unwrap().unwrap().final_fee(); 92 | 93 | let xt = api 94 | .balance_transfer_allow_death(bob.clone().into(), balance_of_alice - transaction_fee - 1) 95 | .await 96 | .unwrap(); 97 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).await; 98 | // With allow_death the call should succeed 99 | assert!(result.is_ok()); 100 | 101 | let alice_account = api.get_account_data(&alice).await.unwrap(); 102 | // Alice account should not exist anymore so we excpect an error 103 | assert!(alice_account.is_none()); 104 | 105 | let balance_of_bob = api.get_account_data(&bob).await.unwrap().unwrap_or_default().free; 106 | println!("[+] Bob's Free Balance is {}\n", balance_of_bob); 107 | } 108 | -------------------------------------------------------------------------------- /testing/async/examples/pallet_transaction_payment_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the pallet transaction payment interface functions. 17 | 18 | use codec::Encode; 19 | use sp_keyring::Sr25519Keyring; 20 | use substrate_api_client::{ 21 | ac_primitives::RococoRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::JsonrpseeClient, Api, 22 | GetChainInfo, GetTransactionPayment, 23 | }; 24 | 25 | #[tokio::main] 26 | async fn main() { 27 | // Setup 28 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 29 | let alice_pair = Sr25519Keyring::Alice.pair(); 30 | let mut api = Api::::new(client).await.unwrap(); 31 | api.set_signer(alice_pair.into()); 32 | 33 | let bob = Sr25519Keyring::Bob.to_account_id(); 34 | 35 | let block_hash = api.get_block_hash(None).await.unwrap().unwrap(); 36 | let encoded_xt = api 37 | .balance_transfer_allow_death(bob.into(), 1000000000000) 38 | .await 39 | .unwrap() 40 | .encode(); 41 | 42 | // Tests 43 | let _fee_details = api 44 | .get_fee_details(&encoded_xt.clone().into(), Some(block_hash)) 45 | .await 46 | .unwrap() 47 | .unwrap(); 48 | let _payment_info = api 49 | .get_payment_info(&encoded_xt.into(), Some(block_hash)) 50 | .await 51 | .unwrap() 52 | .unwrap(); 53 | } 54 | -------------------------------------------------------------------------------- /testing/async/examples/state_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the state rpc interface functions. 17 | 18 | use codec::Decode; 19 | use pallet_balances::AccountData as GenericAccountData; 20 | use pallet_society::Vote; 21 | use sp_core::{crypto::Ss58Codec, sr25519}; 22 | use sp_keyring::Sr25519Keyring; 23 | use substrate_api_client::{ 24 | ac_primitives::{Config, RococoRuntimeConfig}, 25 | rpc::JsonrpseeClient, 26 | Api, GetChainInfo, GetStorage, 27 | }; 28 | 29 | type KitchensinkConfig = RococoRuntimeConfig; 30 | type Balance = ::Balance; 31 | type AccountData = GenericAccountData; 32 | 33 | #[tokio::main] 34 | async fn main() { 35 | // Setup 36 | let client = JsonrpseeClient::with_default_url().await.unwrap(); 37 | let api = Api::::new(client).await.unwrap(); 38 | 39 | let alice = Sr25519Keyring::Alice.to_account_id(); 40 | let block_hash = api.get_block_hash(None).await.unwrap().unwrap(); 41 | let alice_stash = 42 | sr25519::Public::from_ss58check("5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY") 43 | .unwrap(); 44 | 45 | // Tests 46 | let _total_issuance: Balance = 47 | api.get_storage("Balances", "TotalIssuance", None).await.unwrap().unwrap(); 48 | let _total_issuance: Balance = api 49 | .get_storage("Balances", "TotalIssuance", Some(block_hash)) 50 | .await 51 | .unwrap() 52 | .unwrap(); 53 | let _account_info: AccountData = 54 | api.get_storage_map("System", "Account", &alice, None).await.unwrap().unwrap(); 55 | 56 | let votes: Option = api 57 | .get_storage_double_map("Society", "DefenderVotes", 0, alice_stash, None) 58 | .await 59 | .unwrap(); 60 | assert!(votes.is_none()); 61 | 62 | // Ensure the prefix matches the actual storage key: 63 | let storage_key_prefix = api.get_storage_map_key_prefix("System", "Account").await.unwrap(); 64 | let storage_key = api.metadata().storage_map_key("System", "Account", &alice).unwrap(); 65 | 66 | let prefix_len = storage_key_prefix.0.len(); 67 | assert_eq!(storage_key_prefix.0, storage_key.0[..prefix_len]); 68 | 69 | let _account_data: AccountData = 70 | api.get_storage_by_key(storage_key.clone(), None).await.unwrap().unwrap(); 71 | let account_data_opaque = 72 | api.get_opaque_storage_by_key(storage_key.clone(), None).await.unwrap().unwrap(); 73 | let _account_data = AccountData::decode(&mut account_data_opaque.as_slice()).unwrap(); 74 | let _value_proof = api 75 | .get_storage_value_proof("Balances", "TotalIssuance", None) 76 | .await 77 | .unwrap() 78 | .unwrap(); 79 | let _map_proof = api 80 | .get_storage_map_proof("System", "Account", &alice, None) 81 | .await 82 | .unwrap() 83 | .unwrap(); 84 | let _double_map_proof = api 85 | .get_storage_double_map_proof("Society", "DefenderVotes", 0, &alice, None) 86 | .await 87 | .unwrap() 88 | .unwrap(); 89 | let _storage_proof = api 90 | .get_storage_proof_by_keys(vec![storage_key.clone()], None) 91 | .await 92 | .unwrap() 93 | .unwrap(); 94 | let _keys = api.get_keys(storage_key, None).await.unwrap().unwrap(); 95 | let _constants: Balance = api.get_constant("Balances", "ExistentialDeposit").await.unwrap(); 96 | 97 | let max_keys = 2003; 98 | let result = api 99 | .get_storage_keys_paged_limited(Some(storage_key_prefix.clone()), max_keys, None, None) 100 | .await; 101 | assert!(result.is_err()); 102 | assert!(format!("{result:?}").contains("count exceeds maximum value")); 103 | 104 | let storage_keys = api 105 | .get_storage_keys_paged(Some(storage_key_prefix), max_keys, None, None) 106 | .await 107 | .unwrap(); 108 | assert_eq!(storage_keys.len() as u32, 13); 109 | 110 | let max_keys = 20; 111 | let storage_keys = 112 | api.get_storage_keys_paged_limited(None, max_keys, None, None).await.unwrap(); 113 | assert_eq!(storage_keys.len() as u32, max_keys); 114 | 115 | let storage_keys = api.get_storage_keys_paged(None, max_keys, None, None).await.unwrap(); 116 | assert_eq!(storage_keys.len() as u32, max_keys); 117 | } 118 | -------------------------------------------------------------------------------- /testing/sync/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac-testing-sync" 3 | version = "1.18.0" 4 | authors = ["Supercomputing Systems AG "] 5 | license = "Apache-2.0" 6 | edition = "2021" 7 | 8 | [dev-dependencies] 9 | # Substrate dependencies 10 | sp-application-crypto = { workspace = true } 11 | sp-core = { workspace = true } 12 | sp-runtime = { workspace = true } 13 | 14 | # local deps 15 | substrate-api-client = { workspace = true, features = ["tungstenite-client", "ws-client"] } 16 | ac-keystore = { workspace = true } 17 | -------------------------------------------------------------------------------- /testing/sync/examples/keystore_tests.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | //! Tests for the author rpc interface functions. 17 | 18 | use ac_keystore::{Keystore, KeystoreExt, LocalKeystore}; 19 | use sp_application_crypto::sr25519; 20 | use sp_core::crypto::{KeyTypeId, Ss58Codec}; 21 | use std::path::PathBuf; 22 | 23 | pub const KEYSTORE_PATH: &str = "my_keystore"; 24 | pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25"); 25 | 26 | fn main() { 27 | let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); 28 | let seed = "//Ferdie"; 29 | 30 | // This does not place the key into the keystore if we have a seed, but it does 31 | // place it into the keystore if the seed is none. 32 | let key = store.sr25519_generate_new(SR25519, Some(seed)).unwrap(); 33 | store.insert(SR25519, seed, &key.0).unwrap(); 34 | 35 | drop(store); 36 | println!("{}", key.to_ss58check()); 37 | 38 | let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); 39 | let pubkeys = store.public_keys::().unwrap(); 40 | 41 | assert_eq!(pubkeys[0].to_ss58check(), key.to_ss58check()); 42 | } 43 | -------------------------------------------------------------------------------- /testing/sync/examples/tungstenite_client_test.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Supercomputing Systems AG 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | use sp_core::{ 17 | crypto::{Pair, Ss58Codec}, 18 | sr25519, 19 | }; 20 | use sp_runtime::MultiAddress; 21 | use substrate_api_client::{ 22 | ac_primitives::RococoRuntimeConfig, extrinsic::BalancesExtrinsics, rpc::TungsteniteRpcClient, 23 | Api, GetAccountInformation, SubmitAndWatch, XtStatus, 24 | }; 25 | 26 | fn main() { 27 | // Setup 28 | let alice: sr25519::Pair = Pair::from_string( 29 | "0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a", 30 | None, 31 | ) 32 | .unwrap(); 33 | let client = TungsteniteRpcClient::with_default_url(100); 34 | let mut api = Api::::new(client).unwrap(); 35 | api.set_signer(alice.clone().into()); 36 | 37 | let bob = sr25519::Public::from_ss58check("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty") 38 | .unwrap(); 39 | let bob_balance = api.get_account_data(&bob.into()).unwrap().unwrap_or_default().free; 40 | 41 | // Check for failed extrinsic failed onchain 42 | let xt = api 43 | .balance_transfer_allow_death(MultiAddress::Id(bob.into()), bob_balance + 1) 44 | .unwrap(); 45 | let result = api.submit_and_watch_extrinsic_until(xt.clone(), XtStatus::InBlock); 46 | assert!(format!("{result:?}").contains("FundsUnavailable")); 47 | 48 | // Check directly failed extrinsic (before actually submitted to a block) 49 | let result = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock); 50 | assert!(result.is_err()); 51 | assert!(format!("{result:?}").contains("ExtrinsicFailed")); 52 | 53 | // Check for successful extrinisc 54 | let xt = api 55 | .balance_transfer_allow_death(MultiAddress::Id(bob.into()), bob_balance / 2) 56 | .unwrap(); 57 | let _block_hash = api 58 | .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) 59 | .unwrap() 60 | .block_hash 61 | .unwrap(); 62 | let bob_new_balance = api.get_account_data(&bob.into()).unwrap().unwrap().free; 63 | assert!(bob_new_balance > bob_balance); 64 | } 65 | --------------------------------------------------------------------------------