You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中从自定义向量或signed_payload.encode()实现类似PolkadotJS createType的SignedPayload?

从自定义向量创建SignedPayload & Rust中类似PolkadotJS createType的实现

Great question! Let's break this down into two clear, practical parts with Rust code tailored to Substrate's ecosystem.

1. 从自定义向量创建SignedPayload

First, let's clarify: the vector you get from signed_payload.encode() is the SCALE-encoded byte stream of the SignedPayload struct. To recreate the SignedPayload from this vector, you just need to reverse the encoding process via deserialization—using Substrate's standard parity-scale-codec tools.

Step 1: Define your core types and required traits

Make sure your call type, signed extra parameters, and SignedPayload implement Encode/Decode (derive macros handle most of this automatically):

use parity_scale_codec::{Encode, Decode};
use sp_runtime::{
    generic::SignedPayload,
    DispatchError,
    generic::Era,
};

// Example custom call type (matches your chain's runtime calls)
#[derive(Encode, Decode, Clone, Debug)]
enum MyChainCall {
    Transfer { to: [u8; 32], amount: u128 },
    StakingBond { validator: [u8; 32], amount: u128 },
}

// Signed extra parameters (era, nonce, tip) required for valid signatures
#[derive(Encode, Decode, Clone, Debug)]
struct MySignedExtra {
    era: Era,
    nonce: u32,
    tip: u128,
}

// Implement the `SignedPayload` trait to define how payloads are packed for signing
impl sp_runtime::traits::SignedPayload<MyChainCall> for MySignedExtra {
    type Error = DispatchError;

    fn signed_payload(&self, call: &MyChainCall) -> Vec<u8> {
        // This defines the exact data that gets signed
        (call, self.era, self.nonce, self.tip).encode()
    }
}

Step 2: Deserialize the vector into a SignedPayload

Use Decode (or DecodeAll for stricter validation) to turn the byte vector back into a SignedPayload instance:

fn reconstruct_signed_payload(encoded_vec: Vec<u8>) -> Result<SignedPayload<MyChainCall, MySignedExtra>, parity_scale_codec::Error> {
    let mut decoder = &encoded_vec[..];
    SignedPayload::decode(&mut decoder)
}

// Usage example:
// let original_payload = SignedPayload::new(my_call, my_signed_extra).unwrap();
// let encoded = original_payload.encode();
// let reconstructed = reconstruct_signed_payload(encoded)?;

2. Replicating PolkadotJS's createType in Rust

PolkadotJS's createType is a dynamic tool that lets you instantiate types from string names or decode bytes into arbitrary types. Since Rust is statically typed, we can't replicate full dynamic behavior, but we can build a type-safe equivalent that covers the core use cases.

Option 1: Generic static decoder (for known types)

If you know the target type at compile time, use a generic function to decode bytes into any type implementing DecodeAll:

use parity_scale_codec::DecodeAll;

// Mimics `createType` for decoding bytes into a pre-defined type
fn create_type<T: DecodeAll>(encoded_bytes: Vec<u8>) -> Result<T, parity_scale_codec::Error> {
    T::decode_all(&encoded_bytes[..])
}

// Usage:
// let recovered_payload: SignedPayload<MyChainCall, MySignedExtra> = create_type(encoded_vec)?;

Option 2: Dynamic type mapping (for runtime type selection)

If you need to decode based on a type string (like PolkadotJS's dynamic lookup), use an enum to encapsulate all possible types and map names to decoding logic:

#[derive(Encode, Decode, Debug)]
enum RuntimeType {
    SignedPayload(SignedPayload<MyChainCall, MySignedExtra>),
    ChainCall(MyChainCall),
    SignedExtra(MySignedExtra),
}

fn create_type_by_name(type_name: &str, encoded_bytes: Vec<u8>) -> Result<RuntimeType, String> {
    match type_name {
        "SignedPayload" => Ok(RuntimeType::SignedPayload(
            SignedPayload::decode_all(&encoded_bytes[..])
                .map_err(|e| format!("Decoding failed: {}", e))?
        )),
        "MyChainCall" => Ok(RuntimeType::ChainCall(
            MyChainCall::decode_all(&encoded_bytes[..])
                .map_err(|e| format!("Decoding failed: {}", e))?
        )),
        "MySignedExtra" => Ok(RuntimeType::SignedExtra(
            MySignedExtra::decode_all(&encoded_bytes[..])
                .map_err(|e| format!("Decoding failed: {}", e))?
        )),
        _ => Err(format!("Unknown type name: {}", type_name)),
    }
}

Key Notes

  • Always ensure your types implement Encode and Decode—derive macros work for most cases, but you can implement them manually for custom logic.
  • Substrate's built-in types (like Era, Index, AccountId32) already have these traits implemented, so you can use them directly.
  • Prefer DecodeAll over Decode for stricter validation—it ensures the entire byte stream is consumed, avoiding leftover or invalid data.

内容的提问来源于stack exchange,提问作者whalelephant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 09:12:56