Skip to content

Commit 0feb0c2

Browse files
authored
Merge pull request #223 from BitGo/BTC-3062/remove-no-metadata-fallback
chore: remove unreachable no-metadata fallback paths
2 parents d135d0c + 7256e84 commit 0feb0c2

File tree

2 files changed

+42
-147
lines changed

2 files changed

+42
-147
lines changed

packages/wasm-dot/src/parser.rs

Lines changed: 35 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22
//!
33
//! Parses raw extrinsic bytes into structured data.
44
//!
5-
//! Supports two modes of pallet/method resolution:
6-
//! - **Dynamic (metadata-based)**: Uses runtime metadata to resolve pallet and call names
7-
//! from their indices. This is the preferred approach as it handles runtime upgrades
8-
//! and chain-specific index differences automatically.
9-
//! - **Hardcoded fallback**: Uses a static mapping of known pallet/method indices.
10-
//! Used when no metadata is provided in the parsing context.
5+
//! Uses runtime metadata to resolve pallet and call names from their indices.
6+
//! Metadata is required (enforced at the TypeScript level in `fromHex`/`fromBytes`).
117
128
use crate::address::encode_ss58;
139
use crate::error::WasmDotError;
@@ -148,44 +144,6 @@ fn resolve_call_from_metadata(
148144
Some((pallet_name, call_name))
149145
}
150146

151-
/// Resolve pallet and call names using the hardcoded static mapping.
152-
///
153-
/// This is the fallback when no metadata is available. It covers known
154-
/// pallet indices for Polkadot, Kusama, and Westend.
155-
fn resolve_call_hardcoded(pallet_index: u8, method_index: u8) -> (&'static str, &'static str) {
156-
match (pallet_index, method_index) {
157-
// Balances pallet
158-
// Polkadot: 5, Kusama: 4, Westend: 10
159-
(4, 0) | (5, 0) | (10, 0) => ("balances", "transfer"),
160-
(4, 3) | (5, 3) | (10, 3) => ("balances", "transferKeepAlive"),
161-
(4, 4) | (5, 4) | (10, 4) => ("balances", "transferAll"),
162-
163-
// Staking pallet
164-
// Polkadot: 7, Kusama: 6, Westend: 8
165-
(6, 0) | (7, 0) | (8, 0) => ("staking", "bond"),
166-
(6, 1) | (7, 1) | (8, 1) => ("staking", "bondExtra"),
167-
(6, 2) | (7, 2) | (8, 2) => ("staking", "unbond"),
168-
(6, 3) | (7, 3) | (8, 3) => ("staking", "withdrawUnbonded"),
169-
(6, 6) | (7, 6) | (8, 6) => ("staking", "chill"),
170-
(6, 18) | (7, 18) | (8, 18) => ("staking", "payoutStakers"),
171-
172-
// Proxy pallet
173-
// Polkadot: 29, Kusama: 29, Westend: 30
174-
(29, 0) | (30, 0) => ("proxy", "proxy"),
175-
(29, 1) | (30, 1) => ("proxy", "addProxy"),
176-
(29, 2) | (30, 2) => ("proxy", "removeProxy"),
177-
(29, 4) | (30, 4) => ("proxy", "createPure"),
178-
179-
// Utility pallet
180-
// Polkadot: 26, Kusama: 24, Westend: 16
181-
(16, 0) | (24, 0) | (26, 0) => ("utility", "batch"),
182-
(16, 2) | (24, 2) | (26, 2) => ("utility", "batchAll"),
183-
184-
// Unknown
185-
_ => ("unknown", "unknown"),
186-
}
187-
}
188-
189147
/// Convert snake_case to camelCase.
190148
///
191149
/// Examples:
@@ -213,8 +171,8 @@ fn snake_to_camel(s: &str) -> String {
213171

214172
/// Parse call data into method info.
215173
///
216-
/// When metadata is provided, uses dynamic resolution via `pallet_by_index()` and
217-
/// `call_variant_by_index()`. Falls back to hardcoded mapping otherwise.
174+
/// Uses metadata for dynamic resolution via `pallet_by_index()` and
175+
/// `call_variant_by_index()`. Metadata is required.
218176
fn parse_call_data(
219177
call_data: &[u8],
220178
address_prefix: u16,
@@ -247,16 +205,19 @@ fn parse_call_data_with_size(
247205
let method_index = call_data[1];
248206
let args_data = &call_data[2..];
249207

250-
// Resolve pallet and method names: prefer metadata, fall back to hardcoded
251-
let (pallet, name) = if let Some(md) = metadata {
252-
resolve_call_from_metadata(md, pallet_index, method_index).unwrap_or_else(|| {
253-
let (p, n) = resolve_call_hardcoded(pallet_index, method_index);
254-
(p.to_string(), n.to_string())
255-
})
256-
} else {
257-
let (p, n) = resolve_call_hardcoded(pallet_index, method_index);
258-
(p.to_string(), n.to_string())
259-
};
208+
// Resolve pallet and method names from metadata (required)
209+
let md = metadata.ok_or_else(|| {
210+
WasmDotError::InvalidTransaction(
211+
"Metadata required to resolve pallet/method names".to_string(),
212+
)
213+
})?;
214+
let (pallet, name) =
215+
resolve_call_from_metadata(md, pallet_index, method_index).ok_or_else(|| {
216+
WasmDotError::InvalidTransaction(format!(
217+
"Unknown pallet index {} method index {} in metadata",
218+
pallet_index, method_index
219+
))
220+
})?;
260221

261222
// Parse args based on method, getting bytes consumed
262223
let (args, args_consumed) =
@@ -465,7 +426,7 @@ fn parse_proxy_args(
465426
));
466427
}
467428

468-
let proxy_type = resolve_proxy_type(args[cursor], metadata);
429+
let proxy_type = resolve_proxy_type(args[cursor], metadata)?;
469430
cursor += 1;
470431

471432
let delay = u32::from_le_bytes([
@@ -496,7 +457,7 @@ fn parse_create_pure_args(
496457
"truncated createPure args".to_string(),
497458
));
498459
}
499-
let proxy_type = resolve_proxy_type(args[0], metadata);
460+
let proxy_type = resolve_proxy_type(args[0], metadata)?;
500461
let delay = u32::from_le_bytes([args[1], args[2], args[3], args[4]]);
501462
let index = u16::from_le_bytes([args[5], args[6]]);
502463

@@ -563,7 +524,7 @@ fn parse_proxy_proxy_args(
563524
"truncated proxy type".to_string(),
564525
));
565526
}
566-
let pt = resolve_proxy_type(args[cursor], metadata);
527+
let pt = resolve_proxy_type(args[cursor], metadata)?;
567528
cursor += 1;
568529
Some(pt)
569530
} else {
@@ -616,28 +577,20 @@ fn parse_multi_address(args: &[u8], address_prefix: u16) -> Result<(String, usiz
616577
}
617578
}
618579

619-
/// Resolve proxy type name from metadata, falling back to hardcoded Polkadot mainnet mapping.
580+
/// Resolve proxy type name from metadata. Metadata is required.
620581
fn resolve_proxy_type(
621582
proxy_type_byte: u8,
622583
metadata: Option<&subxt_core::metadata::Metadata>,
623-
) -> String {
624-
if let Some(md) = metadata {
625-
if let Some(name) = resolve_proxy_type_from_metadata(md, proxy_type_byte) {
626-
return name;
627-
}
628-
}
629-
// Fallback: Polkadot mainnet proxy type indices
630-
match proxy_type_byte {
631-
0 => "Any".to_string(),
632-
1 => "NonTransfer".to_string(),
633-
2 => "Governance".to_string(),
634-
3 => "Staking".to_string(),
635-
4 => "IdentityJudgement".to_string(),
636-
5 => "CancelProxy".to_string(),
637-
6 => "Auction".to_string(),
638-
7 => "NominationPools".to_string(),
639-
_ => format!("Unknown({})", proxy_type_byte),
640-
}
584+
) -> Result<String, WasmDotError> {
585+
let md = metadata.ok_or_else(|| {
586+
WasmDotError::InvalidTransaction("Metadata required to resolve proxy type".to_string())
587+
})?;
588+
resolve_proxy_type_from_metadata(md, proxy_type_byte).ok_or_else(|| {
589+
WasmDotError::InvalidTransaction(format!(
590+
"Unknown proxy type index {} in metadata",
591+
proxy_type_byte
592+
))
593+
})
641594
}
642595

643596
/// Look up the ProxyType enum variant name from chain metadata.
@@ -764,47 +717,10 @@ mod tests {
764717
}
765718

766719
#[test]
767-
fn test_resolve_hardcoded_polkadot_balances() {
768-
assert_eq!(
769-
resolve_call_hardcoded(5, 3),
770-
("balances", "transferKeepAlive")
771-
);
772-
}
773-
774-
#[test]
775-
fn test_resolve_hardcoded_kusama_balances() {
776-
assert_eq!(
777-
resolve_call_hardcoded(4, 3),
778-
("balances", "transferKeepAlive")
779-
);
780-
}
781-
782-
#[test]
783-
fn test_resolve_hardcoded_westend_balances() {
784-
assert_eq!(
785-
resolve_call_hardcoded(10, 3),
786-
("balances", "transferKeepAlive")
787-
);
788-
}
789-
790-
#[test]
791-
fn test_resolve_hardcoded_unknown() {
792-
assert_eq!(resolve_call_hardcoded(255, 255), ("unknown", "unknown"));
793-
}
794-
795-
#[test]
796-
fn test_parse_call_data_without_metadata() {
797-
// Polkadot balances::transferKeepAlive (pallet=5, method=3) with a dummy address + amount
798-
let mut call_data = vec![5u8, 3u8]; // pallet=5, method=3
799-
call_data.push(0x00); // MultiAddress::Id variant
800-
call_data.extend_from_slice(&[0u8; 32]); // dummy pubkey
801-
call_data.push(0x04); // compact amount = 1
802-
803-
let result = parse_call_data(&call_data, 42, None).unwrap();
804-
assert_eq!(result.pallet, "balances");
805-
assert_eq!(result.name, "transferKeepAlive");
806-
assert_eq!(result.pallet_index, 5);
807-
assert_eq!(result.method_index, 3);
720+
fn test_parse_call_data_without_metadata_returns_error() {
721+
let call_data = vec![5u8, 3u8, 0x00];
722+
let result = parse_call_data(&call_data, 42, None);
723+
assert!(result.is_err());
808724
}
809725

810726
#[test]

packages/wasm-dot/src/transaction.rs

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -674,40 +674,19 @@ fn parse_signing_payload(
674674

675675
/// Parse signed extensions from extrinsic bytes.
676676
///
677-
/// When metadata is available, iterates the runtime's signed extension list
678-
/// and decodes each extension by its type ID. This handles runtimes with
679-
/// extra extensions like CheckMetadataHash or ChargeAssetTxPayment.
680-
///
681-
/// Without metadata, falls back to the hardcoded layout: era + nonce + tip.
677+
/// Iterates the runtime's signed extension list from metadata and decodes
678+
/// each extension by its type ID. This handles runtimes with extra extensions
679+
/// like CheckMetadataHash or ChargeAssetTxPayment. Metadata is required.
682680
///
683681
/// Returns (era, nonce, tip, bytes_consumed).
684682
fn parse_signed_extensions(
685683
bytes: &[u8],
686684
metadata: Option<&Metadata>,
687685
) -> Result<(Era, u32, u128, usize), WasmDotError> {
688-
use parity_scale_codec::{Compact, Decode};
689-
690-
if let Some(md) = metadata {
691-
parse_signed_extensions_from_metadata(bytes, md)
692-
} else {
693-
// Fallback: hardcoded era + Compact<u32> nonce + Compact<u128> tip
694-
let mut cursor = 0;
695-
696-
let (era, era_size) = decode_era_bytes(&bytes[cursor..])?;
697-
cursor += era_size;
698-
699-
let mut input = &bytes[cursor..];
700-
let nonce = <Compact<u32>>::decode(&mut input)
701-
.map_err(|e| WasmDotError::InvalidTransaction(format!("Invalid nonce: {}", e)))?;
702-
cursor = bytes.len() - input.len();
703-
704-
let mut input = &bytes[cursor..];
705-
let tip = <Compact<u128>>::decode(&mut input)
706-
.map_err(|e| WasmDotError::InvalidTransaction(format!("Invalid tip: {}", e)))?;
707-
cursor = bytes.len() - input.len();
708-
709-
Ok((era, nonce.0, tip.0, cursor))
710-
}
686+
let md = metadata.ok_or_else(|| {
687+
WasmDotError::InvalidTransaction("Metadata required to parse signed extensions".to_string())
688+
})?;
689+
parse_signed_extensions_from_metadata(bytes, md)
711690
}
712691

713692
/// Parse signed extensions using metadata to determine the layout.

0 commit comments

Comments
 (0)