subxt_core/view_functions/
payload.rs

1// Copyright 2019-2024 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 module contains the trait and types used to represent
6//! View Function calls that can be made.
7
8use alloc::vec::Vec;
9use core::marker::PhantomData;
10use derive_where::derive_where;
11use scale_encode::EncodeAsFields;
12use scale_value::Composite;
13
14use crate::Error;
15use crate::dynamic::DecodedValueThunk;
16use crate::error::MetadataError;
17
18use crate::metadata::{DecodeWithMetadata, Metadata};
19
20/// This represents a View Function payload that can call into the runtime of node.
21///
22/// # Components
23///
24/// - associated return type
25///
26/// Resulting bytes of the call are interpreted into this type.
27///
28/// - query ID
29///
30/// The ID used to identify in the runtime which view function to call.
31///
32/// - encoded arguments
33///
34/// Each argument of the View Function must be scale-encoded.
35pub trait Payload {
36    /// The return type of the function call.
37    // Note: `DecodeWithMetadata` is needed to decode the function call result
38    // with the `subxt::Metadata.
39    type ReturnType: DecodeWithMetadata;
40
41    /// The payload target.
42    fn query_id(&self) -> &[u8; 32];
43
44    /// Scale encode the arguments data.
45    fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error>;
46
47    /// Encode arguments data and return the output. This is a convenience
48    /// wrapper around [`Payload::encode_args_to`].
49    fn encode_args(&self, metadata: &Metadata) -> Result<Vec<u8>, Error> {
50        let mut v = Vec::new();
51        self.encode_args_to(metadata, &mut v)?;
52        Ok(v)
53    }
54
55    /// Returns the statically generated validation hash.
56    fn validation_hash(&self) -> Option<[u8; 32]> {
57        None
58    }
59}
60
61/// A View Function payload containing the generic argument data
62/// and interpreting the result of the call as `ReturnTy`.
63///
64/// This can be created from static values (ie those generated
65/// via the `subxt` macro) or dynamic values via [`dynamic`].
66#[derive_where(Clone, Debug, Eq, Ord, PartialEq, PartialOrd; ArgsData)]
67pub struct DefaultPayload<ArgsData, ReturnTy> {
68    query_id: [u8; 32],
69    args_data: ArgsData,
70    validation_hash: Option<[u8; 32]>,
71    _marker: PhantomData<ReturnTy>,
72}
73
74/// A statically generated View Function payload.
75pub type StaticPayload<ArgsData, ReturnTy> = DefaultPayload<ArgsData, ReturnTy>;
76/// A dynamic View Function payload.
77pub type DynamicPayload = DefaultPayload<Composite<()>, DecodedValueThunk>;
78
79impl<ArgsData: EncodeAsFields, ReturnTy: DecodeWithMetadata> Payload
80    for DefaultPayload<ArgsData, ReturnTy>
81{
82    type ReturnType = ReturnTy;
83
84    fn query_id(&self) -> &[u8; 32] {
85        &self.query_id
86    }
87
88    fn encode_args_to(&self, metadata: &Metadata, out: &mut Vec<u8>) -> Result<(), Error> {
89        let view_function = metadata
90            .view_function_by_query_id(&self.query_id)
91            .ok_or(MetadataError::ViewFunctionNotFound(self.query_id))?;
92        let mut fields = view_function
93            .inputs()
94            .map(|input| scale_encode::Field::named(input.ty, &input.name));
95
96        self.args_data
97            .encode_as_fields_to(&mut fields, metadata.types(), out)?;
98
99        Ok(())
100    }
101
102    fn validation_hash(&self) -> Option<[u8; 32]> {
103        self.validation_hash
104    }
105}
106
107impl<ReturnTy, ArgsData> DefaultPayload<ArgsData, ReturnTy> {
108    /// Create a new [`DefaultPayload`] for a View Function call.
109    pub fn new(query_id: [u8; 32], args_data: ArgsData) -> Self {
110        DefaultPayload {
111            query_id,
112            args_data,
113            validation_hash: None,
114            _marker: PhantomData,
115        }
116    }
117
118    /// Create a new static [`DefaultPayload`] for a View Function call
119    /// using static function name and scale-encoded argument data.
120    ///
121    /// This is only expected to be used from codegen.
122    #[doc(hidden)]
123    pub fn new_static(
124        query_id: [u8; 32],
125        args_data: ArgsData,
126        hash: [u8; 32],
127    ) -> DefaultPayload<ArgsData, ReturnTy> {
128        DefaultPayload {
129            query_id,
130            args_data,
131            validation_hash: Some(hash),
132            _marker: core::marker::PhantomData,
133        }
134    }
135
136    /// Do not validate this call prior to submitting it.
137    pub fn unvalidated(self) -> Self {
138        Self {
139            validation_hash: None,
140            ..self
141        }
142    }
143
144    /// Returns the arguments data.
145    pub fn args_data(&self) -> &ArgsData {
146        &self.args_data
147    }
148}
149
150/// Create a new [`DynamicPayload`] to call a View Function.
151pub fn dynamic(query_id: [u8; 32], args_data: impl Into<Composite<()>>) -> DynamicPayload {
152    DefaultPayload::new(query_id, args_data.into())
153}