55 */
66import type { EthSigner } from '@aztec/ethereum' ;
77import { Buffer32 } from '@aztec/foundation/buffer' ;
8- import { Secp256k1Signer , toRecoveryBit } from '@aztec/foundation/crypto' ;
8+ import { Secp256k1Signer , randomBytes , toRecoveryBit } from '@aztec/foundation/crypto' ;
99import type { EthAddress } from '@aztec/foundation/eth-address' ;
1010import { Signature , type ViemTransactionSignature } from '@aztec/foundation/eth-signature' ;
11+ import { jsonStringify } from '@aztec/foundation/json-rpc' ;
1112import { withHexPrefix } from '@aztec/foundation/string' ;
1213
1314import {
@@ -27,7 +28,7 @@ import type { EthRemoteSignerConfig } from './types.js';
2728export class SignerError extends Error {
2829 constructor (
2930 message : string ,
30- public method : 'eth_sign' | 'eth_signTransaction' | 'eth_signTypedData_v4' ,
31+ public method : string ,
3132 public url : string ,
3233 public statusCode ?: number ,
3334 public errorCode ?: number ,
@@ -128,50 +129,12 @@ export class RemoteSigner implements EthSigner {
128129 * Make a JSON-RPC eth_sign request.
129130 */
130131 private async makeJsonRpcSignRequest ( data : Buffer32 ) : Promise < Signature > {
131- const url = this . getSignerUrl ( ) ;
132-
133- const body = {
134- jsonrpc : '2.0' ,
135- method : 'eth_sign' ,
136- params : [ this . address . toString ( ) , data . toString ( ) ] ,
137- id : 1 ,
138- } ;
139-
140- const response = await this . fetch ( url , {
141- method : 'POST' ,
142- headers : {
143- 'Content-Type' : 'application/json' ,
144- } ,
145- body : JSON . stringify ( body ) ,
146- } ) ;
147-
148- if ( ! response . ok ) {
149- const errorText = await response . text ( ) ;
150- throw new SignerError (
151- `Web3Signer request failed for eth_sign at ${ url } : ${ response . status } ${ response . statusText } - ${ errorText } ` ,
152- 'eth_sign' ,
153- url ,
154- response . status ,
155- ) ;
156- }
157-
158- const result = await response . json ( ) ;
159-
160- if ( result . error ) {
161- throw new SignerError (
162- `Web3Signer JSON-RPC error for eth_sign at ${ url } : ${ result . error . code } - ${ result . error . message } ` ,
163- 'eth_sign' ,
164- url ,
165- undefined ,
166- result . error . code ,
167- ) ;
168- }
132+ let signatureHex = await this . makeJsonRpcRequest ( 'eth_sign' , this . address . toString ( ) , data . toString ( ) ) ;
169133
170- if ( ! result . result ) {
171- throw new Error ( 'Invalid response from Web3Signer: no result found ' ) ;
134+ if ( typeof signatureHex !== 'string' ) {
135+ throw new Error ( 'Invalid signature ' ) ;
172136 }
173137
174- let signatureHex = result . result ;
175138 if ( ! signatureHex . startsWith ( '0x' ) ) {
176139 signatureHex = '0x' + signatureHex ;
177140 }
@@ -183,50 +146,12 @@ export class RemoteSigner implements EthSigner {
183146 * Make a JSON-RPC eth_signTypedData_v4 request.
184147 */
185148 private async makeJsonRpcSignTypedDataRequest ( typedData : TypedDataDefinition ) : Promise < Signature > {
186- const url = this . getSignerUrl ( ) ;
187-
188- const body = {
189- jsonrpc : '2.0' ,
190- method : 'eth_signTypedData_v4' ,
191- params : [ this . address . toString ( ) , typedData ] ,
192- id : 1 ,
193- } ;
194-
195- const response = await this . fetch ( url , {
196- method : 'POST' ,
197- headers : {
198- 'Content-Type' : 'application/json' ,
199- } ,
200- body : JSON . stringify ( body ) ,
201- } ) ;
202-
203- if ( ! response . ok ) {
204- const errorText = await response . text ( ) ;
205- throw new SignerError (
206- `Web3Signer request failed for eth_signTypedData_v4 at ${ url } : ${ response . status } ${ response . statusText } - ${ errorText } ` ,
207- 'eth_signTypedData_v4' ,
208- url ,
209- response . status ,
210- ) ;
211- }
212-
213- const result = await response . json ( ) ;
214-
215- if ( result . error ) {
216- throw new SignerError (
217- `Web3Signer JSON-RPC error for eth_signTypedData_v4 at ${ url } : ${ result . error . code } - ${ result . error . message } ` ,
218- 'eth_signTypedData_v4' ,
219- url ,
220- undefined ,
221- result . error . code ,
222- ) ;
223- }
149+ let signatureHex = await this . makeJsonRpcRequest ( 'eth_signTypedData' , this . address . toString ( ) , typedData ) ;
224150
225- if ( ! result . result ) {
226- throw new Error ( 'Invalid response from Web3Signer: no result found ' ) ;
151+ if ( typeof signatureHex !== 'string' ) {
152+ throw new Error ( 'Invalid signature ' ) ;
227153 }
228154
229- let signatureHex = result . result ;
230155 if ( ! signatureHex . startsWith ( '0x' ) ) {
231156 signatureHex = '0x' + signatureHex ;
232157 }
@@ -242,8 +167,6 @@ export class RemoteSigner implements EthSigner {
242167 throw new Error ( 'This signer does not support tx type: ' + tx . type ) ;
243168 }
244169
245- const url = this . getSignerUrl ( ) ;
246-
247170 const txObject : RemoteSignerTxObject = {
248171 from : this . address . toString ( ) ,
249172 to : tx . to ?? null ,
@@ -263,26 +186,49 @@ export class RemoteSigner implements EthSigner {
263186 // blobs: tx.blobs?.map(blob => (typeof blob === 'string' ? blob : bufferToHex(Buffer.from(blob)))),
264187 } ;
265188
266- const body = {
267- jsonrpc : '2.0' ,
268- method : 'eth_signTransaction' ,
269- params : [ txObject ] ,
270- id : 1 ,
271- } ;
189+ let rawTxHex = await this . makeJsonRpcRequest ( 'eth_signTransaction' , txObject ) ;
190+
191+ if ( typeof rawTxHex !== 'string' ) {
192+ throw new Error ( 'Invalid signed tx' ) ;
193+ }
194+
195+ if ( ! rawTxHex . startsWith ( '0x' ) ) {
196+ rawTxHex = '0x' + rawTxHex ;
197+ }
198+
199+ // we get back to whole signed tx. Deserialize it in order to read the signature
200+ const parsedTxWithSignature = parseTransaction ( rawTxHex ) ;
201+ if (
202+ parsedTxWithSignature . r === undefined ||
203+ parsedTxWithSignature . s === undefined ||
204+ parsedTxWithSignature . v === undefined
205+ ) {
206+ throw new Error ( 'Tx not signed by Web3Signer' ) ;
207+ }
208+
209+ return Signature . fromViemTransactionSignature ( parsedTxWithSignature as ViemTransactionSignature ) ;
210+ }
211+
212+ /**
213+ * Sends a JSON-RPC request and returns its result
214+ */
215+ private async makeJsonRpcRequest ( method : string , ...params : any [ ] ) : Promise < any > {
216+ const url = this . getSignerUrl ( ) ;
217+ const id = this . generateId ( ) ;
272218
273219 const response = await this . fetch ( url , {
274220 method : 'POST' ,
275221 headers : {
276222 'Content-Type' : 'application/json' ,
277223 } ,
278- body : JSON . stringify ( body ) ,
224+ body : jsonStringify ( { jsonrpc : '2.0' , method , params , id } ) ,
279225 } ) ;
280226
281227 if ( ! response . ok ) {
282228 const errorText = await response . text ( ) ;
283229 throw new SignerError (
284- `Web3Signer request failed for eth_signTransaction at ${ url } : ${ response . status } ${ response . statusText } - ${ errorText } ` ,
285- 'eth_signTransaction' ,
230+ `Web3Signer request failed for ${ method } at ${ url } : ${ response . status } ${ response . statusText } - ${ errorText } ` ,
231+ method ,
286232 url ,
287233 response . status ,
288234 ) ;
@@ -292,10 +238,10 @@ export class RemoteSigner implements EthSigner {
292238
293239 if ( result . error ) {
294240 throw new SignerError (
295- `Web3Signer JSON-RPC error for eth_signTransaction at ${ url } : ${ result . error . code } - ${ result . error . message } ` ,
296- 'eth_signTransaction' ,
241+ `Web3Signer JSON-RPC error for ${ method } at ${ url } : ${ result . error . code } - ${ result . error . message } ` ,
242+ method ,
297243 url ,
298- undefined ,
244+ response . status ,
299245 result . error . code ,
300246 ) ;
301247 }
@@ -304,22 +250,7 @@ export class RemoteSigner implements EthSigner {
304250 throw new Error ( 'Invalid response from Web3Signer: no result found' ) ;
305251 }
306252
307- let rawTxHex = result . result ;
308- if ( ! rawTxHex . startsWith ( '0x' ) ) {
309- rawTxHex = '0x' + rawTxHex ;
310- }
311-
312- // we get back to whole signed tx. Deserialize it in order to read the signature
313- const parsedTxWithSignature = parseTransaction ( rawTxHex ) ;
314- if (
315- parsedTxWithSignature . r === undefined ||
316- parsedTxWithSignature . s === undefined ||
317- parsedTxWithSignature . v === undefined
318- ) {
319- throw new Error ( 'Tx not signed by Web3Signer' ) ;
320- }
321-
322- return Signature . fromViemTransactionSignature ( parsedTxWithSignature as ViemTransactionSignature ) ;
253+ return result . result ;
323254 }
324255
325256 /**
@@ -331,4 +262,11 @@ export class RemoteSigner implements EthSigner {
331262 }
332263 return this . config . remoteSignerUrl ;
333264 }
265+
266+ /**
267+ * Generate an id to use for a JSON-RPC call
268+ */
269+ private generateId ( ) : string {
270+ return randomBytes ( 4 ) . toString ( 'hex' ) ;
271+ }
334272}
0 commit comments