@@ -8,11 +8,9 @@ mod calls;
88pub mod types;
99
1010use crate :: error:: WasmDotError ;
11- use crate :: transaction:: { encode_era , Transaction } ;
11+ use crate :: transaction:: Transaction ;
1212use crate :: types:: { Era , Validity } ;
1313use calls:: encode_intent;
14- use parity_scale_codec:: { Compact , Encode } ;
15- use subxt_core:: metadata:: Metadata ;
1614use types:: { BuildContext , TransactionIntent } ;
1715
1816/// Build a transaction from a business-level intent and context.
@@ -33,17 +31,10 @@ pub fn build_transaction(
3331 // Calculate era from validity
3432 let era = compute_era ( & context. validity ) ;
3533
36- // Build unsigned extrinsic with signed extensions encoded per the chain's metadata
37- let unsigned_bytes = build_unsigned_extrinsic (
38- & call_data,
39- & era,
40- context. nonce ,
41- context. tip as u128 ,
42- & metadata,
43- ) ?;
44-
45- // Create transaction from bytes — pass metadata so parser uses metadata-aware decoding
46- let mut tx = Transaction :: from_bytes ( & unsigned_bytes, None , Some ( & metadata) ) ?;
34+ // Create transaction directly from components (no extrinsic encoding needed).
35+ // to_bytes() on unsigned transactions returns signable_payload(), which is the
36+ // signing payload format: call_data | era | nonce | tip | extensions | additional_signed.
37+ let mut tx = Transaction :: new ( call_data, era, context. nonce , context. tip as u128 ) ;
4738 tx. set_context ( context. material , context. validity , & context. reference_block ) ?;
4839
4940 Ok ( tx)
@@ -63,138 +54,6 @@ fn compute_era(validity: &Validity) -> Era {
6354 }
6455}
6556
66- /// Build unsigned extrinsic bytes with metadata-driven signed extension encoding.
67- ///
68- /// Iterates the chain's signed extension list from metadata and encodes each:
69- /// - Empty types (0-size composites/tuples): skip
70- /// - CheckMortality: era bytes
71- /// - CheckNonce: Compact<u32>
72- /// - ChargeTransactionPayment: Compact<u128> tip
73- /// - ChargeAssetTxPayment: Compact<u128> tip + 0x00 (None asset_id)
74- /// - CheckMetadataHash: 0x00 (Disabled mode)
75- /// - Other non-empty types: encode default bytes using scale_decode to determine size
76- fn build_unsigned_extrinsic (
77- call_data : & [ u8 ] ,
78- era : & Era ,
79- nonce : u32 ,
80- tip : u128 ,
81- metadata : & Metadata ,
82- ) -> Result < Vec < u8 > , WasmDotError > {
83- let mut body = Vec :: new ( ) ;
84-
85- // Version byte: 0x04 = unsigned, version 4
86- body. push ( 0x04 ) ;
87-
88- // Encode signed extensions per metadata
89- for ext in metadata. extrinsic ( ) . signed_extensions ( ) {
90- let id = ext. identifier ( ) ;
91- let ty_id = ext. extra_ty ( ) ;
92-
93- if is_empty_type ( metadata, ty_id) {
94- continue ;
95- }
96-
97- match id {
98- "CheckMortality" | "CheckEra" => {
99- body. extend_from_slice ( & encode_era ( era) ) ;
100- }
101- "CheckNonce" => {
102- Compact ( nonce) . encode_to ( & mut body) ;
103- }
104- "ChargeTransactionPayment" => {
105- Compact ( tip) . encode_to ( & mut body) ;
106- }
107- "ChargeAssetTxPayment" => {
108- // Struct: { tip: Compact<u128>, asset_id: Option<T> }
109- Compact ( tip) . encode_to ( & mut body) ;
110- body. push ( 0x00 ) ; // None — no asset_id
111- }
112- "CheckMetadataHash" => {
113- // Mode enum: 0x00 = Disabled
114- body. push ( 0x00 ) ;
115- }
116- _ => {
117- // Unknown non-empty extension — encode zero bytes.
118- // This shouldn't happen for known chains but is a safety fallback.
119- encode_zero_value ( & mut body, ty_id, metadata) ?;
120- }
121- }
122- }
123-
124- // Call data
125- body. extend_from_slice ( call_data) ;
126-
127- // Length prefix (compact encoded)
128- let mut result = Compact ( body. len ( ) as u32 ) . encode ( ) ;
129- result. extend_from_slice ( & body) ;
130-
131- Ok ( result)
132- }
133-
134- /// Check if a type ID resolves to an empty (zero-size) type.
135- fn is_empty_type ( metadata : & Metadata , ty_id : u32 ) -> bool {
136- let Some ( ty) = metadata. types ( ) . resolve ( ty_id) else {
137- return false ;
138- } ;
139- match & ty. type_def {
140- scale_info:: TypeDef :: Tuple ( t) => t. fields . is_empty ( ) ,
141- scale_info:: TypeDef :: Composite ( c) => c. fields . is_empty ( ) ,
142- _ => false ,
143- }
144- }
145-
146- /// Encode the zero/default value for a type. Used for unknown signed extensions
147- /// where we don't know the semantic meaning but need to produce valid SCALE bytes.
148- fn encode_zero_value (
149- buf : & mut Vec < u8 > ,
150- ty_id : u32 ,
151- metadata : & Metadata ,
152- ) -> Result < ( ) , WasmDotError > {
153- let Some ( ty) = metadata. types ( ) . resolve ( ty_id) else {
154- return Ok ( ( ) ) ; // Unknown type — skip
155- } ;
156- match & ty. type_def {
157- scale_info:: TypeDef :: Primitive ( p) => {
158- use scale_info:: TypeDefPrimitive ;
159- let zeros: usize = match p {
160- TypeDefPrimitive :: Bool | TypeDefPrimitive :: U8 | TypeDefPrimitive :: I8 => 1 ,
161- TypeDefPrimitive :: U16 | TypeDefPrimitive :: I16 => 2 ,
162- TypeDefPrimitive :: U32 | TypeDefPrimitive :: I32 => 4 ,
163- TypeDefPrimitive :: U64 | TypeDefPrimitive :: I64 => 8 ,
164- TypeDefPrimitive :: U128 | TypeDefPrimitive :: I128 => 16 ,
165- TypeDefPrimitive :: U256 | TypeDefPrimitive :: I256 => 32 ,
166- TypeDefPrimitive :: Str | TypeDefPrimitive :: Char => {
167- buf. push ( 0x00 ) ; // empty compact-encoded string/char
168- return Ok ( ( ) ) ;
169- }
170- } ;
171- buf. extend_from_slice ( & vec ! [ 0u8 ; zeros] ) ;
172- }
173- scale_info:: TypeDef :: Compact ( _) => {
174- buf. push ( 0x00 ) ; // Compact(0)
175- }
176- scale_info:: TypeDef :: Variant ( v) => {
177- // Use first variant (index 0 or lowest)
178- if let Some ( variant) = v. variants . first ( ) {
179- buf. push ( variant. index ) ;
180- for field in & variant. fields {
181- encode_zero_value ( buf, field. ty . id , metadata) ?;
182- }
183- }
184- }
185- scale_info:: TypeDef :: Composite ( c) => {
186- for field in & c. fields {
187- encode_zero_value ( buf, field. ty . id , metadata) ?;
188- }
189- }
190- scale_info:: TypeDef :: Sequence ( _) | scale_info:: TypeDef :: Array ( _) => {
191- buf. push ( 0x00 ) ; // empty sequence
192- }
193- _ => { } // BitSequence, etc. — skip
194- }
195- Ok ( ( ) )
196- }
197-
19857#[ cfg( test) ]
19958mod tests {
20059 // Tests require real metadata - will be added with test fixtures
0 commit comments