Skip to main content

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}