1
1
//! Some specifics of using the [`wasm_bindgen`] library:
2
2
//!
3
- //! # Currently only `Result<T, JsValue>` is allowed
4
- //! [tracking issue]: https://github.com/rustwasm/wasm-bindgen/issues/1004
5
- //!
6
3
//! # JavaScript enums do not support methods at all
7
4
//! [tracking issue]: https://github.com/rustwasm/wasm-bindgen/issues/1715
8
5
//!
@@ -21,18 +18,27 @@ use mm2_rpc::data::legacy::MmVersionResponse;
21
18
use mm2_rpc:: wasm_rpc:: WasmRpcResponse ;
22
19
use serde:: { Deserialize , Serialize } ;
23
20
use serde_json:: Value as Json ;
24
-
25
- /// The errors can be thrown when using the `mm2_main` function incorrectly.
26
21
#[ wasm_bindgen]
27
- #[ derive( Primitive ) ]
28
- pub enum Mm2MainErr {
29
- AlreadyRuns = 1 ,
30
- InvalidParams = 2 ,
31
- NoCoinsInConf = 3 ,
22
+ #[ derive( Debug , Clone , Serialize ) ]
23
+ struct StartupError {
24
+ code : StartupErrorCode ,
25
+ message : String ,
32
26
}
33
27
34
- impl From < Mm2MainErr > for JsValue {
35
- fn from ( e : Mm2MainErr ) -> Self { JsValue :: from ( e as i32 ) }
28
+ #[ wasm_bindgen]
29
+ impl StartupError {
30
+ fn new ( code : StartupErrorCode , message : impl Into < String > ) -> Self {
31
+ Self {
32
+ code,
33
+ message : message. into ( ) ,
34
+ }
35
+ }
36
+
37
+ #[ wasm_bindgen( getter) ]
38
+ pub fn code ( & self ) -> i8 { self . code . clone ( ) as i8 }
39
+
40
+ #[ wasm_bindgen( getter) ]
41
+ pub fn message ( & self ) -> String { self . message . clone ( ) }
36
42
}
37
43
38
44
#[ derive( Deserialize ) ]
@@ -58,7 +64,7 @@ impl From<MainParams> for LpMainParams {
58
64
/// # Usage
59
65
///
60
66
/// ```javascript
61
- /// import init, {mm2_main, LogLevel, Mm2MainErr } from "./path/to/mm2.js";
67
+ /// import init, {mm2_main, LogLevel, StartupErrorCode } from "./path/to/mm2.js";
62
68
///
63
69
/// const params = {
64
70
/// conf: { "gui":"WASMTEST", mm2:1, "passphrase":"YOUR_PASSPHRASE_HERE", "rpc_password":"test123", "coins":[{"coin":"ETH","protocol":{"type":"ETH"}}] },
@@ -68,8 +74,8 @@ impl From<MainParams> for LpMainParams {
68
74
/// try {
69
75
/// mm2_main(params, handle_log);
70
76
/// } catch (e) {
71
- /// switch (e) {
72
- /// case Mm2MainErr.AlreadyRuns :
77
+ /// switch (e.code ) {
78
+ /// case StartupErrorCode.AlreadyRunning :
73
79
/// alert("MarketMaker2 already runs...");
74
80
/// break;
75
81
/// // handle other errors...
@@ -80,51 +86,70 @@ impl From<MainParams> for LpMainParams {
80
86
/// }
81
87
/// ```
82
88
#[ wasm_bindgen]
83
- pub fn mm2_main ( params : JsValue , log_cb : js_sys:: Function ) -> Result < ( ) , JsValue > {
89
+ pub async fn mm2_main ( params : JsValue , log_cb : js_sys:: Function ) -> Result < i8 , JsValue > {
84
90
let params: MainParams = match deserialize_from_js ( params. clone ( ) ) {
85
91
Ok ( p) => p,
86
92
Err ( e) => {
87
- console_err ! ( "Expected 'MainParams' as the first argument, found {:?}: {}" , params, e) ;
88
- return Err ( Mm2MainErr :: InvalidParams . into ( ) ) ;
93
+ let error = StartupError :: new (
94
+ StartupErrorCode :: InvalidParams ,
95
+ format ! ( "Expected 'MainParams' as the first argument, found {:?}: {}" , params, e) ,
96
+ ) ;
97
+ console_err ! ( "{}" , error. message( ) ) ;
98
+ return Err ( error. into ( ) ) ;
89
99
} ,
90
100
} ;
91
101
if params. conf [ "coins" ] . is_null ( ) {
92
- console_err ! ( "Config must contain 'coins' field: {:?}" , params. conf) ;
93
- return Err ( Mm2MainErr :: NoCoinsInConf . into ( ) ) ;
102
+ let error = StartupError :: new (
103
+ StartupErrorCode :: ConfigError ,
104
+ format ! ( "Config must contain 'coins' field: {:?}" , params. conf) ,
105
+ ) ;
106
+ console_err ! ( "{}" , error. message( ) ) ;
107
+ return Err ( error. into ( ) ) ;
94
108
}
95
109
let params = LpMainParams :: from ( params) ;
96
110
97
111
if LP_MAIN_RUNNING . load ( Ordering :: Relaxed ) {
98
- return Err ( Mm2MainErr :: AlreadyRuns . into ( ) ) ;
112
+ let error = StartupError :: new ( StartupErrorCode :: AlreadyRunning , "MM2 is already running" ) ;
113
+ console_err ! ( "{}" , error. message( ) ) ;
114
+ return Err ( error. into ( ) ) ;
99
115
}
100
116
CTX . store ( 0 , Ordering :: Relaxed ) ; // Remove the old context ID during restarts.
101
117
102
118
register_callback ( WasmCallback :: with_js_function ( log_cb) ) ;
119
+ // Setting up a global panic hook to log panic information to the console
120
+ // This doesn't prevent termination of the WebAssembly instance, but ensures error details are visible
121
+ // We can't use catch_unwind directly with async/await in WebAssembly, so this is our best option for diagnostics
122
+ // If a panic occurs, the MM2 instance will terminate but the browser tab will remain responsive
103
123
set_panic_hook ( ) ;
104
124
105
- let fut = async move {
106
- if let Err ( true ) = LP_MAIN_RUNNING . compare_exchange ( false , true , Ordering :: Relaxed , Ordering :: Relaxed ) {
107
- console_err ! ( "lp_main already started!" ) ;
108
- return ;
109
- }
110
- let ctx_cb = |ctx| CTX . store ( ctx , Ordering :: Relaxed ) ;
111
- // TODO figure out how to use catch_unwind here
112
- // use futures::FutureExt;
113
- // match mm2_main::lp_main(params, &ctx_cb).catch_unwind().await {
114
- // Ok(Ok(_)) => console_info!("run_lp_main finished"),
115
- // Ok(Err(err)) => console_err!("run_lp_main error: {}", err),
116
- // Err(err) => console_err!("run_lp_main panic: {:?}", any_to_str(&*err)) ,
117
- // };
118
- match mm2_main :: lp_main ( params , & ctx_cb , KDF_VERSION . into ( ) , KDF_DATETIME . into ( ) ) . await {
119
- Ok ( ( ) ) => console_info ! ( "run_lp_main finished" ) ,
120
- Err ( err ) => console_err ! ( "run_lp_main error: {}" , err ) ,
121
- } ;
122
- LP_MAIN_RUNNING . store ( false , Ordering :: Relaxed )
125
+ if let Err ( true ) = LP_MAIN_RUNNING . compare_exchange ( false , true , Ordering :: Relaxed , Ordering :: Relaxed ) {
126
+ let error = StartupError :: new ( StartupErrorCode :: AlreadyRunning , "lp_main already started!" ) ;
127
+ console_err ! ( "{}" , error . message ( ) ) ;
128
+ return Err ( error . into ( ) ) ;
129
+ }
130
+
131
+ let ctx_cb = |ctx| CTX . store ( ctx , Ordering :: Relaxed ) ;
132
+ let ctx = match mm2_main :: lp_main ( params , & ctx_cb , KDF_VERSION . into ( ) , KDF_DATETIME . into ( ) ) . await {
133
+ Ok ( ctx ) => {
134
+ console_info ! ( "run_lp_main finished" ) ;
135
+ ctx
136
+ } ,
137
+ Err ( err ) => {
138
+ let error = StartupError :: new ( StartupErrorCode :: InitError , format ! ( "run_lp_main error: {}" , err ) ) ;
139
+ console_err ! ( "{}" , error . message ( ) ) ;
140
+ LP_MAIN_RUNNING . store ( false , Ordering :: Relaxed ) ;
141
+ return Err ( error . into ( ) ) ;
142
+ } ,
123
143
} ;
124
144
125
- // At this moment we still don't have `MmCtx` context to use its `MmCtx::abortable_system` spawner.
126
- executor:: spawn_local ( fut) ;
127
- Ok ( ( ) )
145
+ executor:: spawn_local ( async move {
146
+ if let Err ( e) = mm2_main:: lp_run ( ctx) . await {
147
+ console_err ! ( "MM2 runtime error: {}" , e) ;
148
+ }
149
+ LP_MAIN_RUNNING . store ( false , Ordering :: Relaxed ) ;
150
+ } ) ;
151
+
152
+ Ok ( StartupErrorCode :: Ok as i8 )
128
153
}
129
154
130
155
/// Returns the MarketMaker2 instance status.
0 commit comments