mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-01 13:37:57 +00:00
Browser extension signing example (#1067)
* routing and signing example * cliipy fix * submitting extrinsics * change order of lines * Skip call variants if there aren't any (#980) Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> * Tidy up some metadata accessing (#978) * Reduce some repetition when obtaining metadata pallets/runtime_traits * make them pub * fix docs and clippy * Bump tokio from 1.28.1 to 1.28.2 (#984) Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.28.1 to 1.28.2. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.28.1...tokio-1.28.2) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump regex from 1.8.2 to 1.8.3 (#986) Bumps [regex](https://github.com/rust-lang/regex) from 1.8.2 to 1.8.3. - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.8.2...1.8.3) --- updated-dependencies: - dependency-name: regex dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump quote from 1.0.27 to 1.0.28 (#983) Bumps [quote](https://github.com/dtolnay/quote) from 1.0.27 to 1.0.28. - [Release notes](https://github.com/dtolnay/quote/releases) - [Commits](https://github.com/dtolnay/quote/compare/1.0.27...1.0.28) --- updated-dependencies: - dependency-name: quote dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump proc-macro2 from 1.0.58 to 1.0.59 (#985) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.58 to 1.0.59. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.58...1.0.59) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * restrict sign_with_address_and_signature interface (#988) * changing js bridge * dryrunresult ok * submitting extrinsic working * tiny up code and ui * formatting * remove todos * support tip and mortality * Prevent bug when reusing type ids in hashing (#1075) * practice TDD * implement a hashmap 2-phases approach * use nicer types * add test for cache filling * adjust test --------- Co-authored-by: James Wilson <james@jsdw.me> * small adjustment * Merge branch 'master' into tadeo-hepperle-browser-extension-signing-example * fix lock file * tell users how to add Alice account to run signing example * adjust to PR comments * fmt --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: James Wilson <james@jsdw.me> Co-authored-by: Niklas Adolfsson <niklasadolfsson1@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,19 @@
|
||||
use anyhow::anyhow;
|
||||
use futures::StreamExt;
|
||||
use js_sys::Promise;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use std::fmt::Write;
|
||||
use subxt::ext::codec::Encode;
|
||||
use subxt::tx::PartialExtrinsic;
|
||||
use subxt::{self, OnlineClient, PolkadotConfig};
|
||||
use subxt::utils::AccountId32;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use yew::{AttrValue, Callback};
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "../../artifacts/polkadot_metadata_small.scale")]
|
||||
mod polkadot {}
|
||||
pub mod polkadot {}
|
||||
|
||||
pub(crate) async fn fetch_constant_block_length() -> Result<String, subxt::Error> {
|
||||
let api = OnlineClient::<PolkadotConfig>::new().await?;
|
||||
@@ -71,3 +80,105 @@ pub(crate) async fn subscribe_to_finalized_blocks(
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_name = getAccounts)]
|
||||
pub fn js_get_accounts() -> Promise;
|
||||
#[wasm_bindgen(js_name = signPayload)]
|
||||
pub fn js_sign_payload(payload: String, source: String, address: String) -> Promise;
|
||||
}
|
||||
|
||||
/// DTO to communicate with JavaScript
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Account {
|
||||
/// account name
|
||||
pub name: String,
|
||||
/// name of the browser extension
|
||||
pub source: String,
|
||||
/// the signature type, e.g. "sr25519" or "ed25519"
|
||||
pub ty: String,
|
||||
/// ss58 formatted address as string. Can be converted into AccountId32 via it's FromStr implementation.
|
||||
pub address: String,
|
||||
}
|
||||
|
||||
pub async fn get_accounts() -> Result<Vec<Account>, anyhow::Error> {
|
||||
let result = JsFuture::from(js_get_accounts())
|
||||
.await
|
||||
.map_err(|js_err| anyhow!("{js_err:?}"))?;
|
||||
let accounts_str = result
|
||||
.as_string()
|
||||
.ok_or(anyhow!("Error converting JsValue into String"))?;
|
||||
let accounts: Vec<Account> = serde_json::from_str(&accounts_str)?;
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
fn to_hex(bytes: impl AsRef<[u8]>) -> String {
|
||||
format!("0x{}", hex::encode(bytes.as_ref()))
|
||||
}
|
||||
|
||||
fn encode_to_hex<E: Encode>(input: &E) -> String {
|
||||
format!("0x{}", hex::encode(input.encode()))
|
||||
}
|
||||
|
||||
/// this is used because numeric types (e.g. u32) are encoded as little-endian via scale (e.g. 9430 -> d6240000)
|
||||
/// while we need a big-endian representation for the json (e.g. 9430 -> 000024d6).
|
||||
fn encode_to_hex_reverse<E: Encode>(input: &E) -> String {
|
||||
let mut bytes = input.encode();
|
||||
bytes.reverse();
|
||||
format!("0x{}", hex::encode(bytes))
|
||||
}
|
||||
|
||||
|
||||
/// communicates with JavaScript to obtain a signature for the `partial_extrinsic` via a browser extension (e.g. polkadot-js or Talisman)
|
||||
///
|
||||
/// Some parameters are hard-coded here and not taken from the partial_extrinsic itself (mortality_checkpoint, era, tip).
|
||||
pub async fn extension_signature_for_partial_extrinsic(
|
||||
partial_extrinsic: &PartialExtrinsic<PolkadotConfig, OnlineClient<PolkadotConfig>>,
|
||||
api: &OnlineClient<PolkadotConfig>,
|
||||
account_id: &AccountId32,
|
||||
account_source: String,
|
||||
account_address: String,
|
||||
) -> Result<Vec<u8>, anyhow::Error> {
|
||||
let spec_version = encode_to_hex_reverse(&api.runtime_version().spec_version);
|
||||
let transaction_version = encode_to_hex_reverse(&api.runtime_version().transaction_version);
|
||||
let mortality_checkpoint = encode_to_hex(&api.genesis_hash());
|
||||
let era = encode_to_hex(&subxt::config::extrinsic_params::Era::Immortal);
|
||||
let genesis_hash = encode_to_hex(&api.genesis_hash());
|
||||
let method = to_hex(partial_extrinsic.call_data());
|
||||
let nonce = api.tx().account_nonce(account_id).await?;
|
||||
let nonce = encode_to_hex_reverse(&nonce);
|
||||
let signed_extensions: Vec<String> = api
|
||||
.metadata()
|
||||
.extrinsic()
|
||||
.signed_extensions()
|
||||
.iter()
|
||||
.map(|e| e.identifier().to_string())
|
||||
.collect();
|
||||
let tip = encode_to_hex(&subxt::config::polkadot::PlainTip::new(0));
|
||||
|
||||
let payload = json!({
|
||||
"specVersion": spec_version,
|
||||
"transactionVersion": transaction_version,
|
||||
"address": account_address,
|
||||
"blockHash": mortality_checkpoint,
|
||||
"blockNumber": "0x00000000",
|
||||
"era": era,
|
||||
"genesisHash": genesis_hash,
|
||||
"method": method,
|
||||
"nonce": nonce,
|
||||
"signedExtensions": signed_extensions,
|
||||
"tip": tip,
|
||||
"version": 4,
|
||||
});
|
||||
|
||||
let payload = payload.to_string();
|
||||
let result = JsFuture::from(js_sign_payload(payload, account_source, account_address))
|
||||
.await
|
||||
.map_err(|js_err| anyhow!("{js_err:?}"))?;
|
||||
let signature = result
|
||||
.as_string()
|
||||
.ok_or(anyhow!("Error converting JsValue into String"))?;
|
||||
let signature = hex::decode(&signature[2..])?;
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user