-
Notifications
You must be signed in to change notification settings - Fork 289
Open
Description
Hi @jsdw,
Opening this issue to share what I'm currently doing since I haven't found a default option on the latest version of subxt and I think it might be good to have something similar available.
I often find it useful to keep the raw bytes exported from here tx().call_data(&proxy_call)? around, and only sign and submit on a second stage. I couldn't find a method on the latest version that could easily decode these bytes, the option tx().from_bytes(encoded) requires the encoded data to be already signed. I have looked to the other methods on how to create partial transaction, but they don't seem to apply here.
Do you have any suggestions?
Sharing the steps and code blocks to demonstrate my case:
- Export call data:
pub fn wrap_call_into_proxy(
api: &ClientAtBlock<SubstrateConfig, OnlineClientAtBlockImpl<SubstrateConfig>>,
call: RuntimeCall,
proxied_account: &AccountId32,
) -> Result<Vec<u8>, Error> {
let proxy_call = node_runtime::tx().proxy().proxy(
(*proxied_account).into(),
Some(ProxyType::NonTransfer),
call,
);
let payload = api.tx().call_data(&proxy_call).boxed()?;
Ok(payload)
}
- With the chain Metadata available, decode the original call data from previous method
wrap_call_into_proxy, sign it with a validKeypairand submit:
async fn sign_and_submit_call_data(
&self,
api: &OnlineClient<SubstrateConfig>,
proxy_signer: &Keypair,
call_data: &[u8],
) -> Result<Response, Error> {
let at_block = api.at_current_block().await.boxed()?;
let metadata = at_block.metadata();
let payload = RawPayload::from_bytes(&metadata, call_data).boxed()?;
let response = api
.tx()
.await
.boxed()?
.sign_and_submit_then_watch_default(&payload, proxy_signer)
.await
.boxed()?;
Ok(Response::transaction_submitted(response))
}
RawPayloadimplementation:
use scale_encode::{EncodeAsFields, Error as EncodeError, FieldIter, TypeResolver};
use subxt::{
transactions::{Payload, ValidationDetails},
Metadata,
};
pub type Bytes = Vec<u8>;
pub struct RawPayload {
pallet_name: String,
call_name: String,
field_bytes: RawFields,
}
pub struct RawFields(Bytes);
impl EncodeAsFields for RawFields {
fn encode_as_fields_to<R: TypeResolver>(
&self,
_fields: &mut dyn FieldIter<'_, R::TypeId>,
_types: &R,
out: &mut Bytes,
) -> Result<(), EncodeError> {
out.extend_from_slice(&self.0);
Ok(())
}
}
impl Payload for RawPayload {
type CallData = RawFields;
fn pallet_name(&self) -> &str {
&self.pallet_name
}
fn call_name(&self) -> &str {
&self.call_name
}
fn call_data(&self) -> &RawFields {
&self.field_bytes
}
fn validation_details(&self) -> Option<ValidationDetails<'_>> {
None
}
}
impl RawPayload {
pub fn from_bytes(metadata: &Metadata, bytes: &[u8]) -> Result<Self, Error> {
let pallet = metadata
.pallet_by_call_index(bytes[0])
.ok_or(Error::PalletNotFound)?;
let call_variant = pallet
.call_variant_by_index(bytes[1])
.ok_or(Error::CallNotFound)?;
Ok(Self {
pallet_name: pallet.name().to_string(),
call_name: call_variant.name.clone(),
field_bytes: RawFields(bytes[2..].to_vec()),
})
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Pallet not found")]
PalletNotFound,
#[error("Call not found")]
CallNotFound,
}
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels