@@ -18,6 +18,7 @@ import {
1818 SessionSigner ,
1919 SessionSignerInvalidReason ,
2020 isImplicitSessionSigner ,
21+ isIncrementCall ,
2122 UsageLimit ,
2223} from './session/index.js'
2324
@@ -130,21 +131,16 @@ export class SessionManager implements SapientSigner {
130131 } ) )
131132 }
132133
133- async findSignersForCalls ( wallet : Address . Address , chainId : number , calls : Payload . Call [ ] ) : Promise < SessionSigner [ ] > {
134- // Only use signers that match the topology
135- const topology = await this . topology
136- const identitySigners = SessionConfig . getIdentitySigners ( topology )
137- if ( identitySigners . length === 0 ) {
138- throw new Error ( 'Identity signers not found' )
139- }
140-
141- // Prioritize implicit signers
142- const availableSigners = [ ...this . _implicitSigners , ...this . _explicitSigners ]
143- if ( availableSigners . length === 0 ) {
144- throw new Error ( 'No signers match the topology' )
145- }
146-
147- // Find supported signers for each call
134+ /**
135+ * Find one signer per call from the given candidate list (first that supports each call).
136+ */
137+ private async findSignersForCallsWithCandidates (
138+ wallet : Address . Address ,
139+ chainId : number ,
140+ calls : Payload . Call [ ] ,
141+ topology : SessionConfig . SessionsTopology ,
142+ availableSigners : SessionSigner [ ] ,
143+ ) : Promise < SessionSigner [ ] > {
148144 const signers : SessionSigner [ ] = [ ]
149145 for ( const call of calls ) {
150146 let supported = false
@@ -173,9 +169,67 @@ export class SessionManager implements SapientSigner {
173169 if ( expiredSupportedSigner ) {
174170 throw new Error ( `Signer supporting call is expired: ${ expiredSupportedSigner . address } ` )
175171 }
176- throw new Error (
177- `No signer supported for call. ` + `Call: to=${ call . to } , data=${ call . data } , value=${ call . value } , ` ,
178- )
172+ throw new Error ( `No signer supported for call. Call: to=${ call . to } , data=${ call . data } , value=${ call . value } , ` )
173+ }
174+ }
175+ return signers
176+ }
177+
178+ async findSignersForCalls ( wallet : Address . Address , chainId : number , calls : Payload . Call [ ] ) : Promise < SessionSigner [ ] > {
179+ const topology = await this . topology
180+ const identitySigners = SessionConfig . getIdentitySigners ( topology )
181+ if ( identitySigners . length === 0 ) {
182+ throw new Error ( 'Identity signers not found' )
183+ }
184+
185+ const availableSigners = [ ...this . _implicitSigners , ...this . _explicitSigners ]
186+ if ( availableSigners . length === 0 ) {
187+ throw new Error ( 'No signers match the topology' )
188+ }
189+
190+ const nonIncrementCalls : Payload . Call [ ] = [ ]
191+ const incrementCalls : Payload . Call [ ] = [ ]
192+ for ( const call of calls ) {
193+ if ( isIncrementCall ( call , this . address ) ) {
194+ incrementCalls . push ( call )
195+ } else {
196+ nonIncrementCalls . push ( call )
197+ }
198+ }
199+
200+ // Find signers for non-increment calls
201+ const nonIncrementSigners =
202+ nonIncrementCalls . length > 0
203+ ? await this . findSignersForCallsWithCandidates ( wallet , chainId , nonIncrementCalls , topology , availableSigners )
204+ : [ ]
205+
206+ let incrementSigners : SessionSigner [ ] = [ ]
207+ if ( incrementCalls . length > 0 ) {
208+ // Find signers for increment calls, preferring signers that signed non-increment calls
209+ const incrementCandidates = [
210+ ...nonIncrementSigners ,
211+ ...availableSigners . filter ( ( s ) => ! nonIncrementSigners . includes ( s ) ) ,
212+ ]
213+ incrementSigners = await this . findSignersForCallsWithCandidates (
214+ wallet ,
215+ chainId ,
216+ incrementCalls ,
217+ topology ,
218+ incrementCandidates ,
219+ )
220+ }
221+
222+ // Merge back in original call order
223+ const signers : SessionSigner [ ] = [ ]
224+ let nonIncrementIndex = 0
225+ let incrementIndex = 0
226+ for ( const call of calls ) {
227+ if ( isIncrementCall ( call , this . address ) ) {
228+ signers . push ( incrementSigners [ incrementIndex ] ! )
229+ incrementIndex ++
230+ } else {
231+ signers . push ( nonIncrementSigners [ nonIncrementIndex ] ! )
232+ nonIncrementIndex ++
179233 }
180234 }
181235 return signers
@@ -191,20 +245,23 @@ export class SessionManager implements SapientSigner {
191245 }
192246 const signers = await this . findSignersForCalls ( wallet , chainId , calls )
193247
194- // Create a map of signers to their associated calls
195- const signerToCalls = new Map < SessionSigner , Payload . Call [ ] > ( )
248+ // Map each signer to only their non-increment calls
249+ const signerToNonIncrementCalls = new Map < SessionSigner , Payload . Call [ ] > ( )
196250 signers . forEach ( ( signer , index ) => {
197251 const call = calls [ index ] !
198- const existingCalls = signerToCalls . get ( signer ) || [ ]
199- signerToCalls . set ( signer , [ ...existingCalls , call ] )
252+ if ( isIncrementCall ( call , this . address ) ) {
253+ return
254+ }
255+ const existing = signerToNonIncrementCalls . get ( signer ) || [ ]
256+ signerToNonIncrementCalls . set ( signer , [ ...existing , call ] )
200257 } )
201258
202- // Prepare increments for each explicit signer with their associated calls
259+ // Prepare increments for each explicit signer from their non-increment calls only
203260 const increments : UsageLimit [ ] = (
204261 await Promise . all (
205- Array . from ( signerToCalls . entries ( ) ) . map ( async ( [ signer , associatedCalls ] ) => {
262+ Array . from ( signerToNonIncrementCalls . entries ( ) ) . map ( async ( [ signer , nonIncrementCalls ] ) => {
206263 if ( isExplicitSessionSigner ( signer ) ) {
207- return signer . prepareIncrements ( wallet , chainId , associatedCalls , this . address , this . _provider ! )
264+ return signer . prepareIncrements ( wallet , chainId , nonIncrementCalls , this . address , this . _provider ! )
208265 }
209266 return [ ]
210267 } ) ,
0 commit comments