@@ -77,7 +77,7 @@ export function toEthSig(
7777 throw new Error ( 'Invalid signature length' ) ;
7878 }
7979
80- // Enforce low S value
80+ // Enforce low `s`
8181
8282 const rBuf = signature . slice ( 0 , 32 ) ;
8383 let sBuf = signature . slice ( 32 , 64 ) ;
@@ -95,23 +95,26 @@ export function toEthSig(
9595 }
9696 }
9797
98- // Recover parity bit
98+ // Recover `v`
99+ // ---------------------------------------------------------------------------
100+ // NOTE: If the signing library provided the parity of R.y, we could compute
101+ // `v` directly and skip the costly ecrecover operation.
102+ // ---------------------------------------------------------------------------
99103
100104 const expectedAddr = publicToAddressHex ( pubKey ) ;
101105
102- for ( const candidateV of [ 0n , 1n ] ) {
106+ const checkParity = ( parity : bigint ) : boolean => {
103107 try {
104- const candidatePubKey = ecrecover ( hash , candidateV + 27n , rBuf , sBuf ) ;
105- if ( publicToAddressHex ( candidatePubKey ) === expectedAddr ) {
106- const vInt = candidateV + 27n ;
107- return concatBytes ( rBuf , sBuf , bigIntToBytes ( vInt ) ) ;
108- }
108+ const candidatePubKey = ecrecover ( hash , parity , rBuf , sBuf ) ;
109+ return publicToAddressHex ( candidatePubKey ) === expectedAddr ;
109110 } catch {
110- // Ignore errors
111+ return false ;
111112 }
112- }
113+ } ;
113114
114- throw new Error ( 'Invalid signature' ) ;
115+ // Ethereum defines `v = parity(R.y) + 27`.
116+ const vInt = checkParity ( 0n ) ? 27n : 28n ;
117+ return concatBytes ( rBuf , sBuf , bigIntToBytes ( vInt ) ) ;
115118}
116119
117120/**
0 commit comments