// Copyright 2019-2025 Parity Technologies (UK) Ltd. // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. use crate::utils::node_runtime; use crate::{subxt_test, test_context}; use core::ops::Deref; use frame_decode::extrinsics::ExtrinsicType; use subxt_signer::sr25519::dev; // TODO: When VerifySignature exists on the substrate kitchensink runtime, // let's try actuallty submitting v4 and v5 signed extrinsics to verify that // they are actually accepted by the node. #[subxt_test] async fn v4_unsigned_encode_decode() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); let md = api.metadata(); let call = node_runtime::tx() .balances() .transfer_allow_death(dev::bob().public_key().into(), 1000); let tx_bytes = api.tx().create_v4_unsigned(&call).unwrap().into_encoded(); let tx_bytes_cursor = &mut &*tx_bytes; let decoded = frame_decode::extrinsics::decode_extrinsic( tx_bytes_cursor, md.deref(), api.metadata().types(), ) .unwrap(); assert_eq!(tx_bytes_cursor.len(), 0); assert_eq!(decoded.version(), 4); assert_eq!(decoded.ty(), ExtrinsicType::Bare); assert_eq!(decoded.pallet_name(), "Balances"); assert_eq!(decoded.call_name(), "transfer_allow_death"); assert!(decoded.signature_payload().is_none()); Ok(()) } #[subxt_test] async fn v5_bare_encode_decode() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); let md = api.metadata(); let call = node_runtime::tx() .balances() .transfer_allow_death(dev::bob().public_key().into(), 1000); let tx_bytes = api.tx().create_v5_bare(&call).unwrap().into_encoded(); let tx_bytes_cursor = &mut &*tx_bytes; let decoded = frame_decode::extrinsics::decode_extrinsic( tx_bytes_cursor, md.deref(), api.metadata().types(), ) .unwrap(); assert_eq!(tx_bytes_cursor.len(), 0); assert_eq!(decoded.version(), 5); assert_eq!(decoded.ty(), ExtrinsicType::Bare); assert_eq!(decoded.pallet_name(), "Balances"); assert_eq!(decoded.call_name(), "transfer_allow_death"); assert!(decoded.transaction_extension_payload().is_none()); assert!(decoded.signature_payload().is_none()); Ok(()) } #[subxt_test] async fn v4_signed_encode_decode() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); let md = api.metadata(); let call = node_runtime::tx() .balances() .transfer_allow_death(dev::bob().public_key().into(), 1000); let tx_bytes = api .tx() .create_v4_partial(&call, &dev::alice().public_key().into(), Default::default()) .await .unwrap() .sign(&dev::alice()) .into_encoded(); let tx_bytes_cursor = &mut &*tx_bytes; let decoded = frame_decode::extrinsics::decode_extrinsic( tx_bytes_cursor, md.deref(), api.metadata().types(), ) .unwrap(); assert_eq!(tx_bytes_cursor.len(), 0); assert_eq!(decoded.version(), 4); assert_eq!(decoded.ty(), ExtrinsicType::Signed); assert_eq!(decoded.pallet_name(), "Balances"); assert_eq!(decoded.call_name(), "transfer_allow_death"); assert!(decoded.signature_payload().is_some()); Ok(()) } #[subxt_test] async fn v5_general_encode_decode() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); let md = api.metadata(); let dummy_signer = dev::alice(); let call = node_runtime::tx() .balances() .transfer_allow_death(dev::bob().public_key().into(), 1000); let tx_bytes = api .tx() .create_v5_partial(&call, &dev::alice().public_key().into(), Default::default()) .await .unwrap() .sign(&dummy_signer) // No signature payload is added, but may be inserted into tx extensions. .into_encoded(); let tx_bytes_cursor = &mut &*tx_bytes; let decoded = frame_decode::extrinsics::decode_extrinsic( tx_bytes_cursor, md.deref(), api.metadata().types(), ) .unwrap(); assert_eq!(tx_bytes_cursor.len(), 0); assert_eq!(decoded.version(), 5); assert_eq!(decoded.ty(), ExtrinsicType::General); assert_eq!(decoded.pallet_name(), "Balances"); assert_eq!(decoded.call_name(), "transfer_allow_death"); assert!(decoded.transaction_extension_payload().is_some()); // v5 general extrinsics have no signature payload; signature in tx extensions: assert!(decoded.signature_payload().is_none()); Ok(()) }