avail_rust_client/block/signed.rs
1use crate::{
2 Client, Error,
3 block::{
4 BlockExtrinsicMetadata,
5 encoded::BlockEncodedExtrinsic,
6 events::{BlockEvents, BlockEventsQuery},
7 extrinsic::BlockExtrinsic,
8 },
9};
10use avail_rust_core::{ExtrinsicSignature, H256, HasHeader, MultiAddress, RpcError};
11use codec::Decode;
12
13// /// View of block extrinsics restricted to signed transactions.
14// pub struct BlockSignedExtrinsicsQuery {
15// xt: BlockExtrinsicsQuery,
16// }
17
18// impl BlockSignedExtrinsicsQuery {
19// /// Builds a signed-transaction view for the specified block.
20// ///
21// /// # Parameters
22// /// - `client`: RPC client used to access extrinsic data.
23// /// - `block_id`: Identifier convertible into `HashStringNumber`.
24// ///
25// /// # Returns
26// /// - `Self`: Helper that only surfaces signed extrinsics.
27// pub fn new(client: Client, block_id: HashStringNumber) -> Self {
28// Self { xt: BlockExtrinsicsQuery::new(client, block_id) }
29// }
30
31// /// Fetches a signed transaction by hash, index, or string identifier.
32// ///
33// /// # Parameters
34// /// - `extrinsic_id`: Identifier used to select the extrinsic.
35// ///
36// /// # Returns
37// /// - `Ok(Some(SignedExtrinsic<T>))`: Matching extrinsic decoded as `T` with a signature.
38// /// - `Ok(None)`: No extrinsic matched the identifier.
39// /// - `Err(Error)`: The extrinsic was unsigned, or the RPC call/decoding failed.
40// ///
41// /// # Side Effects
42// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
43// pub async fn get<T: HasHeader + Decode>(
44// &self,
45// extrinsic_id: impl Into<HashStringNumber>,
46// ) -> Result<Option<BlockSignedExtrinsic<T>>, Error> {
47// let ext = self.xt.get(extrinsic_id).await?;
48// let Some(ext) = ext else {
49// return Ok(None);
50// };
51
52// Ok(Some(ext.as_signed()?))
53// }
54
55// /// Returns the first signed extrinsic that matches the supplied filters.
56// ///
57// /// # Parameters
58// /// - `opts`: Filters describing which signed extrinsics to fetch.
59// ///
60// /// # Returns
61// /// - `Ok(Some(SignedExtrinsic<T>))`: First matching signed extrinsic decoded as `T`.
62// /// - `Ok(None)`: No signed extrinsic satisfied the filters.
63// /// - `Err(Error)`: The extrinsic was unsigned, or the RPC call/decoding failed.
64// ///
65// /// # Side Effects
66// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
67// pub async fn first<T: HasHeader + Decode>(&self, opts: Options) -> Result<Option<BlockSignedExtrinsic<T>>, Error> {
68// let ext = self.xt.first(opts).await?;
69// let Some(ext) = ext else {
70// return Ok(None);
71// };
72
73// Ok(Some(ext.as_signed()?))
74// }
75
76// /// Returns the last signed extrinsic that matches the supplied filters.
77// ///
78// /// # Parameters
79// /// - `opts`: Filters describing which signed extrinsics to fetch.
80// ///
81// /// # Returns
82// /// - `Ok(Some(SignedExtrinsic<T>))`: Final matching signed extrinsic decoded as `T`.
83// /// - `Ok(None)`: No signed extrinsic satisfied the filters.
84// /// - `Err(Error)`: The extrinsic was unsigned, or the RPC call/decoding failed.
85// ///
86// /// # Side Effects
87// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
88// pub async fn last<T: HasHeader + Decode>(&self, opts: Options) -> Result<Option<BlockSignedExtrinsic<T>>, Error> {
89// let ext = self.xt.last(opts).await?;
90// let Some(ext) = ext else {
91// return Ok(None);
92// };
93
94// Ok(Some(ext.as_signed()?))
95// }
96
97// /// Collects every signed extrinsic that matches the supplied filters.
98// ///
99// /// # Parameters
100// /// - `opts`: Filters describing which signed extrinsics to fetch.
101// ///
102// /// # Returns
103// /// - `Ok(Vec<SignedExtrinsic<T>>)`: Zero or more signed extrinsics decoded as `T`.
104// /// - `Err(Error)`: An extrinsic lacked a signature, or the RPC call/decoding failed.
105// ///
106// /// # Side Effects
107// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
108// pub async fn all<T: HasHeader + Decode>(&self, opts: Options) -> Result<Vec<BlockSignedExtrinsic<T>>, Error> {
109// let all = self.xt.all::<T>(opts).await?;
110// let mut result = Vec::with_capacity(all.len());
111// for ext in all {
112// let Some(signature) = ext.signature else {
113// return Err(UserError::Other(
114// "Extrinsic is unsigned; cannot decode it as a signed transaction.".into(),
115// )
116// .into());
117// };
118// result.push(BlockSignedExtrinsic::new(signature, ext.call, ext.metadata));
119// }
120
121// Ok(result)
122// }
123
124// /// Counts matching signed extrinsics.
125// ///
126// /// # Parameters
127// /// - `opts`: Filters describing which signed extrinsics to count.
128// ///
129// /// # Returns
130// /// - `Ok(usize)`: Number of matching signed extrinsics.
131// /// - `Err(Error)`: The RPC call failed.
132// ///
133// /// # Side Effects
134// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
135// pub async fn count<T: HasHeader>(&self, opts: Options) -> Result<usize, Error> {
136// self.xt.count::<T>(opts).await
137// }
138
139// /// Reports whether any signed extrinsic matches the supplied filters.
140// ///
141// /// # Parameters
142// /// - `opts`: Filters describing which signed extrinsics to test.
143// ///
144// /// # Returns
145// /// - `Ok(true)`: At least one matching signed extrinsic exists.
146// /// - `Ok(false)`: No signed extrinsics matched the filters.
147// /// - `Err(Error)`: The RPC call failed.
148// ///
149// /// # Side Effects
150// /// - Performs RPC calls via the decoded-extrinsic helper and may retry according to the retry policy.
151// pub async fn exists<T: HasHeader>(&self, opts: Options) -> Result<bool, Error> {
152// self.xt.exists::<T>(opts).await
153// }
154
155// /// Overrides the retry behaviour for future signed-transaction lookups.
156// ///
157// /// # Parameters
158// /// - `value`: `Some(true)` to force retries, `Some(false)` to disable retries, `None` to inherit the client default.
159// ///
160// /// # Returns
161// /// - `()`: The override is stored for subsequent operations.
162// ///
163// /// # Side Effects
164// /// - Updates the internal retry setting used by follow-up RPC calls.
165// pub fn set_retry_on_error(&mut self, value: Option<bool>) {
166// self.xt.set_retry_on_error(value);
167// }
168
169// /// Reports whether signed-transaction lookups retry after RPC errors.
170// ///
171// /// # Returns
172// /// - `true`: Retries are enabled either explicitly or via the client default.
173// /// - `false`: Retries are disabled.
174// pub fn should_retry_on_error(&self) -> bool {
175// self.xt.should_retry_on_error()
176// }
177// }
178
179/// Block Transaction is the same as Block Signed Extrinsic
180#[derive(Debug, Clone)]
181pub struct BlockSignedExtrinsic<T: HasHeader + Decode> {
182 /// Signature proving authorship of the extrinsic.
183 pub signature: ExtrinsicSignature,
184 /// Decoded runtime call payload.
185 pub call: T,
186 /// Metadata describing where the extrinsic was found.
187 pub metadata: BlockExtrinsicMetadata,
188}
189
190impl<T: HasHeader + Decode> BlockSignedExtrinsic<T> {
191 /// Creates a transaction wrapper from decoded data.
192 ///
193 /// # Parameters
194 /// - `signature`: Signature associated with the extrinsic.
195 /// - `call`: Decoded call payload.
196 /// - `metadata`: Metadata describing the extrinsic context.
197 ///
198 /// # Returns
199 /// - `Self`: Signed extrinsic wrapper containing the provided data.
200 pub fn new(signature: ExtrinsicSignature, call: T, metadata: BlockExtrinsicMetadata) -> Self {
201 Self { signature, call, metadata }
202 }
203
204 /// Fetches events emitted by this transaction.
205 ///
206 /// # Parameters
207 /// - `client`: RPC client used to fetch event data.
208 ///
209 /// # Returns
210 /// - `Ok(AllEvents)`: Wrapper containing events for this extrinsic.
211 /// - `Err(Error)`: Extrinsic emitted no events or the RPC request failed.
212 ///
213 /// # Side Effects
214 /// - Issues RPC requests for event data and may retry according to the client's configuration.
215 pub async fn events(&self, client: Client) -> Result<BlockEvents, Error> {
216 let events = BlockEventsQuery::new(client, self.metadata.block_id)
217 .extrinsic(self.ext_index())
218 .await?;
219
220 if events.is_empty() {
221 return Err(RpcError::ExpectedData("No events found for the requested extrinsic.".into()).into());
222 };
223
224 Ok(events)
225 }
226
227 /// Returns the index of this transaction inside the block.
228 ///
229 /// # Returns
230 /// - `u32`: Index of the extrinsic within the block.
231 ///
232 /// # Side Effects
233 /// - None; reads cached metadata.
234 pub fn ext_index(&self) -> u32 {
235 self.metadata.ext_index
236 }
237
238 /// Returns the transaction hash.
239 ///
240 /// # Returns
241 /// - `H256`: Hash of the extrinsic.
242 ///
243 /// # Side Effects
244 /// - None; reads cached metadata.
245 pub fn ext_hash(&self) -> H256 {
246 self.metadata.ext_hash
247 }
248
249 /// Returns the application id for this transaction.
250 ///
251 /// # Returns
252 /// - `u32`: Application identifier recorded in the signature.
253 ///
254 /// # Side Effects
255 /// - None; reads cached signature information.
256 pub fn app_id(&self) -> u32 {
257 self.signature.extra.app_id
258 }
259
260 /// Returns the signer nonce for this transaction.
261 ///
262 /// # Returns
263 /// - `u32`: Nonce recorded in the signature.
264 ///
265 /// # Side Effects
266 /// - None; reads cached signature information.
267 pub fn nonce(&self) -> u32 {
268 self.signature.extra.nonce
269 }
270
271 /// Returns the paid tip for this transaction.
272 ///
273 /// # Returns
274 /// - `u128`: Tip recorded in the signature.
275 ///
276 /// # Side Effects
277 /// - None; reads cached signature information.
278 pub fn tip(&self) -> u128 {
279 self.signature.extra.tip
280 }
281
282 /// Returns the signer as an ss58 string when available.
283 ///
284 /// # Returns
285 /// - `Some(String)`: SS58-encoded signer address.
286 /// - `None`: Signer address is not stored as an `Id`.
287 ///
288 /// # Side Effects
289 /// - None; reads cached signature information.
290 pub fn ss58_address(&self) -> Option<String> {
291 match &self.signature.address {
292 MultiAddress::Id(x) => Some(std::format!("{}", x)),
293 _ => None,
294 }
295 }
296}
297
298impl<T: HasHeader + Decode> TryFrom<BlockExtrinsic<T>> for BlockSignedExtrinsic<T> {
299 type Error = String;
300
301 /// Converts a decoded extrinsic into a signed extrinsic when a signature is present.
302 ///
303 /// # Parameters
304 /// - `value`: Decoded extrinsic expected to contain a signature.
305 ///
306 /// # Returns
307 /// - `Ok(Self)`: Signed extrinsic carrying the call and metadata.
308 /// - `Err(String)`: The extrinsic was unsigned.
309 fn try_from(value: BlockExtrinsic<T>) -> Result<Self, Self::Error> {
310 let Some(signature) = value.signature else {
311 return Err("Extrinsic is unsigned; expected a signature.")?;
312 };
313
314 Ok(Self::new(signature, value.call, value.metadata))
315 }
316}
317
318impl<T: HasHeader + Decode + Clone> TryFrom<&BlockExtrinsic<T>> for BlockSignedExtrinsic<T> {
319 type Error = String;
320
321 /// Converts a borrowed extrinsic into a signed extrinsic when a signature is present.
322 ///
323 /// # Parameters
324 /// - `value`: Borrowed extrinsic expected to contain a signature.
325 ///
326 /// # Returns
327 /// - `Ok(Self)`: Signed extrinsic with cloned call and metadata.
328 /// - `Err(String)`: The extrinsic was unsigned.
329 fn try_from(value: &BlockExtrinsic<T>) -> Result<Self, Self::Error> {
330 let Some(signature) = &value.signature else {
331 return Err("Extrinsic is unsigned; expected a signature.")?;
332 };
333
334 Ok(Self::new(signature.clone(), value.call.clone(), value.metadata.clone()))
335 }
336}
337
338impl<T: HasHeader + Decode> TryFrom<BlockEncodedExtrinsic> for BlockSignedExtrinsic<T> {
339 type Error = String;
340
341 /// Decodes an encoded extrinsic into a signed extrinsic.
342 ///
343 /// # Parameters
344 /// - `value`: Encoded extrinsic expected to contain a signature.
345 ///
346 /// # Returns
347 /// - `Ok(Self)`: Signed extrinsic decoded from the payload.
348 /// - `Err(String)`: Decoding failed or the extrinsic was unsigned.
349 fn try_from(value: BlockEncodedExtrinsic) -> Result<Self, Self::Error> {
350 let ext = BlockExtrinsic::try_from(value)?;
351 Self::try_from(ext)
352 }
353}
354
355impl<T: HasHeader + Decode> TryFrom<&BlockEncodedExtrinsic> for BlockSignedExtrinsic<T> {
356 type Error = String;
357
358 /// Decodes a borrowed encoded extrinsic into a signed extrinsic.
359 ///
360 /// # Parameters
361 /// - `value`: Borrowed encoded extrinsic expected to contain a signature.
362 ///
363 /// # Returns
364 /// - `Ok(Self)`: Signed extrinsic decoded from the payload.
365 /// - `Err(String)`: Decoding failed or the extrinsic was unsigned.
366 fn try_from(value: &BlockEncodedExtrinsic) -> Result<Self, Self::Error> {
367 let ext = BlockExtrinsic::try_from(value)?;
368 Self::try_from(ext)
369 }
370}