1+ use futures:: FutureExt ;
12use rmcp:: {
23 model:: { CallToolResult , Content , RawContent , RawTextContent } ,
34 schemars, ErrorData as McpError ,
@@ -6,6 +7,8 @@ use serde::Deserialize;
67use subxt:: { OnlineClient , PolkadotConfig } ;
78use subxt_signer:: sr25519:: dev;
89
10+ use crate :: utils:: { mcp_error_internal, mcp_error_invalid_params} ;
11+
912#[ derive( Debug , Deserialize , Clone , schemars:: JsonSchema ) ]
1013pub struct SubmitExtrinsicProperties {
1114 /// The RPC URL to connect to
@@ -25,25 +28,18 @@ pub async fn handle_submit_dev_extrinsic(
2528) -> Result < CallToolResult , McpError > {
2629 let client = OnlineClient :: < PolkadotConfig > :: from_url ( & properties. rpc_url )
2730 . await
28- . map_err ( |e| McpError {
29- code : rmcp:: model:: ErrorCode ( -32603 ) ,
30- message : format ! ( "Failed to connect to chain: {e}" ) . into ( ) ,
31- data : None ,
32- } ) ?;
31+ . map_err ( |e| mcp_error_internal ( format ! ( "Failed to connect to chain: {e}" ) ) ) ?;
3332
3433 let ( scale_args_result, remainder) = scale_value:: stringify:: from_str ( & properties. args ) ;
35- let scale_args = scale_args_result. map_err ( |e| McpError {
36- code : rmcp:: model:: ErrorCode ( -32602 ) ,
37- message : format ! ( "Failed to parse arguments: {e}. See 'substrate:scale-value-format' resource for syntax guide" ) . into ( ) ,
38- data : None ,
39- } ) ?;
34+
35+ let scale_args = scale_args_result. map_err ( |e|
36+ mcp_error_invalid_params ( format ! ( "Failed to parse arguments: {e}. See 'substrate:scale-value-format' resource for syntax guide" ) )
37+ ) ?;
4038
4139 if !remainder. trim ( ) . is_empty ( ) {
42- return Err ( McpError {
43- code : rmcp:: model:: ErrorCode ( -32602 ) ,
44- message : format ! ( "Unexpected content after arguments: '{remainder}'" ) . into ( ) ,
45- data : None ,
46- } ) ;
40+ return Err ( mcp_error_invalid_params ( format ! (
41+ "Unexpected content after parsing arguments: '{remainder}'"
42+ ) ) ) ;
4743 }
4844
4945 // Create composite from the scale value
@@ -52,6 +48,7 @@ pub async fn handle_submit_dev_extrinsic(
5248 scale_value:: ValueDef :: Composite ( composite) => composite,
5349 _ => scale_value:: Composite :: Unnamed ( vec ! [ scale_args] ) ,
5450 } ;
51+ let call_data = subxt:: dynamic:: tx ( & properties. pallet , & properties. call , composite_args) ;
5552
5653 // Get the appropriate signer based on the requested dev account
5754 let signer = match properties. signer . to_lowercase ( ) . as_str ( ) {
@@ -62,54 +59,46 @@ pub async fn handle_submit_dev_extrinsic(
6259 "eve" => dev:: eve ( ) ,
6360 "ferdie" => dev:: ferdie ( ) ,
6461 _ => {
65- return Err ( McpError {
66- code : rmcp:: model:: ErrorCode ( -32602 ) ,
67- message : format ! (
68- "Invalid signer '{}'. Supported signers: alice, bob, charlie, dave, eve, ferdie" ,
69- properties. signer
70- ) . into ( ) ,
71- data : None ,
72- } ) ;
62+ return Err ( mcp_error_invalid_params ( format ! (
63+ "Invalid signer '{}'. Supported signers: alice, bob, charlie, dave, eve, ferdie" ,
64+ properties. signer
65+ ) ) ) ;
7366 }
7467 } ;
7568
76- // Create the dynamic call payload
77- let call_data = subxt:: dynamic:: tx ( & properties. pallet , & properties. call , composite_args) ;
78-
79- // Submit and wait for finalization
80- let tx_progress = client
81- . tx ( )
82- . sign_and_submit_then_watch_default ( & call_data, & signer)
83- . await
84- . map_err ( |e| McpError {
85- code : rmcp:: model:: ErrorCode ( -32603 ) ,
86- message : format ! ( "Failed to submit transaction: {e}" ) . into ( ) ,
87- data : None ,
88- } ) ?;
69+ // NOTE: `sign_and_submit_then_watch_default` panics when call exists and arguments
70+ // are valid SCALE but don't fit the call type. We get around this by catching the
71+ // panic
72+ let tx_progress = std:: panic:: AssertUnwindSafe (
73+ client
74+ . tx ( )
75+ . sign_and_submit_then_watch_default ( & call_data, & signer) ,
76+ )
77+ . catch_unwind ( )
78+ . await
79+ . map_err ( |_| {
80+ mcp_error_internal ( format ! (
81+ "Transaction submission panicked - likely due to invalid call data. \
82+ Please verify the call data matches the expectd format for pallet '{}' and call '{}'",
83+ properties. pallet, properties. call
84+ ) )
85+ } ) ?
86+ . map_err ( |e| mcp_error_internal ( format ! ( "Failed to submit transaction: {e}" ) ) ) ?;
8987
9088 // Wait for the transaction to be finalized and check for success
9189 let tx_events = tx_progress
9290 . wait_for_finalized_success ( )
9391 . await
94- . map_err ( |e| McpError {
95- code : rmcp:: model:: ErrorCode ( -32603 ) ,
96- message : format ! ( "Transaction failed: {e}" ) . into ( ) ,
97- data : None ,
98- } ) ?;
92+ . map_err ( |e| mcp_error_internal ( format ! ( "Transaction failed: {e}" ) ) ) ?;
9993
10094 let mut events_info = Vec :: new ( ) ;
10195 for event in tx_events. iter ( ) {
102- let event = event. map_err ( |e| McpError {
103- code : rmcp:: model:: ErrorCode ( -32603 ) ,
104- message : format ! ( "Failed to decode event: {e}" ) . into ( ) ,
105- data : None ,
106- } ) ?;
107-
108- let fields = event. field_values ( ) . map_err ( |e| McpError {
109- code : rmcp:: model:: ErrorCode ( -32603 ) ,
110- message : format ! ( "Failed to decode event fields: {e}" ) . into ( ) ,
111- data : None ,
112- } ) ?;
96+ let event =
97+ event. map_err ( |e| mcp_error_internal ( format ! ( "Failed to decode event: {e}" ) ) ) ?;
98+
99+ let fields = event
100+ . field_values ( )
101+ . map_err ( |e| mcp_error_internal ( format ! ( "Failed to decode event fields: {e}" ) ) ) ?;
113102
114103 let value = scale_value:: Value {
115104 value : scale_value:: ValueDef :: Composite ( fields) ,
0 commit comments