1- import  {  hasProperty ,  isPlainObject ,  Json  }  from  '@metamask/utils' ; 
1+ import  { 
2+   hasProperty , 
3+   JsonRpcErrorStruct , 
4+   isValidJson , 
5+   isObject , 
6+ }  from  '@metamask/utils' ; 
7+ import  {  is ,  create  }  from  'superstruct' ; 
28import  {  errorCodes ,  errorValues  }  from  './error-constants' ; 
39import  {  EthereumRpcError ,  SerializedEthereumRpcError  }  from  './classes' ; 
410
511const  FALLBACK_ERROR_CODE  =  errorCodes . rpc . internal ; 
6- const  FALLBACK_MESSAGE  =  'Invalid internal error. See "data.originalError" for original value. Please report this bug.' ; 
12+ const  FALLBACK_MESSAGE  = 
13+   'Invalid internal error. See "data.originalError" for original value. Please report this bug.' ; 
714const  FALLBACK_ERROR : SerializedEthereumRpcError  =  { 
815  code : FALLBACK_ERROR_CODE , 
916  message : getMessageFromCode ( FALLBACK_ERROR_CODE ) , 
@@ -70,11 +77,7 @@ export function serializeError(
7077  error : unknown , 
7178  {  fallbackError =  FALLBACK_ERROR ,  shouldIncludeStack =  false  }  =  { } , 
7279) : SerializedEthereumRpcError  { 
73-   if  ( 
74-     ! fallbackError  || 
75-     ! isValidCode ( fallbackError . code )  || 
76-     typeof  fallbackError . message  !==  'string' 
77-   )  { 
80+   if  ( ! is ( fallbackError ,  JsonRpcErrorStruct ) )  { 
7881    throw  new  Error ( 
7982      'Must provide fallback error with integer number code and string message.' , 
8083    ) ; 
@@ -84,49 +87,40 @@ export function serializeError(
8487    return  error . serialize ( ) ; 
8588  } 
8689
87-   const  serialized : Partial < SerializedEthereumRpcError >  =  { } ; 
90+   const  serialized  =  buildError ( 
91+     error , 
92+     fallbackError  as  SerializedEthereumRpcError , 
93+   ) ; 
8894
89-   if  ( 
90-     error  && 
91-     isPlainObject ( error )  && 
92-     hasProperty ( error ,  'code' )  && 
93-     isValidCode ( ( error  as  SerializedEthereumRpcError ) . code ) 
94-   )  { 
95-     const  _error  =  error  as  Partial < SerializedEthereumRpcError > ; 
96-     serialized . code  =  _error . code  as  number ; 
97- 
98-     if  ( _error . message  &&  typeof  _error . message  ===  'string' )  { 
99-       serialized . message  =  _error . message ; 
100- 
101-       if  ( hasProperty ( _error ,  'data' ) )  { 
102-         serialized . data  =  _error . data  ??  null ; 
103-       } 
104-     }  else  { 
105-       serialized . message  =  getMessageFromCode ( 
106-         ( serialized  as  SerializedEthereumRpcError ) . code , 
107-       ) ; 
108- 
109-       // TODO: Verify that the original error is serializable. 
110-       serialized . data  =  {  originalError : assignOriginalError ( error )  }  as  Json ; 
111-     } 
112-   }  else  { 
113-     serialized . code  =  fallbackError . code ; 
114- 
115-     const  message  =  ( error  as  any ) ?. message ; 
116- 
117-     serialized . message  = 
118-       message  &&  typeof  message  ===  'string'  ? message  : fallbackError . message ; 
119- 
120-     // TODO: Verify that the original error is serializable. 
121-     serialized . data  =  {  originalError : assignOriginalError ( error )  }  as  Json ; 
95+   if  ( ! shouldIncludeStack )  { 
96+     delete  serialized . stack ; 
12297  } 
12398
124-   const  stack  =  ( error  as  any ) ?. stack ; 
99+   return  serialized  as  SerializedEthereumRpcError ; 
100+ } 
125101
126-   if  ( shouldIncludeStack  &&  error  &&  stack  &&  typeof  stack  ===  'string' )  { 
127-     serialized . stack  =  stack ; 
102+ /** 
103+  * Constructs a JSON serializable object given an error and a fallbackError. 
104+  * 
105+  * @param  error - The error in question. 
106+  * @param  fallbackError - The fallback error. 
107+  * @returns  A JSON serializable error object. 
108+  */ 
109+ function  buildError ( error : unknown ,  fallbackError : SerializedEthereumRpcError )  { 
110+   if  ( is ( error ,  JsonRpcErrorStruct ) )  { 
111+     return  create ( error ,  JsonRpcErrorStruct ) ; 
128112  } 
129-   return  serialized  as  SerializedEthereumRpcError ; 
113+   // If the original error is an object, we make a copy of it, this should also copy class properties to an object 
114+   const  originalError  =  isObject ( error )  ? Object . assign ( { } ,  error )  : error ; 
115+   const  fallbackWithOriginal  =  { 
116+     ...fallbackError , 
117+     data : {  originalError } , 
118+   } ; 
119+   // We only allow returning originalError if it turns out to be valid JSON 
120+   if  ( isValidJson ( fallbackWithOriginal ) )  { 
121+     return  fallbackWithOriginal ; 
122+   } 
123+   return  fallbackError ; 
130124} 
131125
132126/** 
@@ -138,16 +132,3 @@ export function serializeError(
138132function  isJsonRpcServerError ( code : number ) : boolean  { 
139133  return  code  >=  - 32099  &&  code  <=  - 32000 ; 
140134} 
141- 
142- /** 
143-  * Create a copy of the given value if it's an object, and not an array. 
144-  * 
145-  * @param  error - The value to copy. 
146-  * @returns  The copied value, or the original value if it's not an object. 
147-  */ 
148- function  assignOriginalError ( error : unknown ) : unknown  { 
149-   if  ( error  &&  typeof  error  ===  'object'  &&  ! Array . isArray ( error ) )  { 
150-     return  Object . assign ( { } ,  error ) ; 
151-   } 
152-   return  error ; 
153- } 
0 commit comments