@@ -13,8 +13,8 @@ export const lastUsedNonceKey = (chainId: number, walletAddress: Address) =>
13
13
`nonce:${ chainId } :${ normalizeAddress ( walletAddress ) } ` ;
14
14
15
15
/**
16
- * The "recycled nonces" set stores unsorted nonces to be reused or cancelled.
17
- * Example: [ "25 ", "23 ", "24 " ]
16
+ * The "recycled nonces" sorted set stores nonces to be reused or cancelled, sorted by nonce value .
17
+ * Example: [ "23 ", "24 ", "25 " ]
18
18
*/
19
19
export const recycledNoncesKey = ( chainId : number , walletAddress : Address ) =>
20
20
`nonce-recycled:${ chainId } :${ normalizeAddress ( walletAddress ) } ` ;
@@ -86,15 +86,15 @@ export const acquireNonce = async (
86
86
chainId : number ,
87
87
walletAddress : Address ,
88
88
) : Promise < { nonce : number ; isRecycledNonce : boolean } > => {
89
- // Acquire an recylced nonce, if any.
90
- let nonce = await _acquireRecycledNonce ( chainId , walletAddress ) ;
91
- if ( nonce !== null ) {
92
- return { nonce, isRecycledNonce : true } ;
89
+ // Try to acquire the lowest recycled nonce first
90
+ const recycledNonce = await _acquireRecycledNonce ( chainId , walletAddress ) ;
91
+ if ( recycledNonce !== null ) {
92
+ return { nonce : recycledNonce , isRecycledNonce : true } ;
93
93
}
94
94
95
95
// Else increment the last used nonce.
96
96
const key = lastUsedNonceKey ( chainId , walletAddress ) ;
97
- nonce = await redis . incr ( key ) ;
97
+ let nonce = await redis . incr ( key ) ;
98
98
if ( nonce === 1 ) {
99
99
// If INCR returned 1, the nonce was not set.
100
100
// This may be a newly imported wallet.
@@ -125,22 +125,25 @@ export const recycleNonce = async (
125
125
}
126
126
127
127
const key = recycledNoncesKey ( chainId , walletAddress ) ;
128
- await redis . sadd ( key , nonce . toString ( ) ) ;
128
+ await redis . zadd ( key , nonce , nonce . toString ( ) ) ;
129
129
} ;
130
130
131
131
/**
132
- * Acquires a recycled nonce that is unused.
132
+ * Acquires the lowest recycled nonce that is unused.
133
133
* @param chainId
134
134
* @param walletAddress
135
135
* @returns
136
136
*/
137
137
const _acquireRecycledNonce = async (
138
138
chainId : number ,
139
139
walletAddress : Address ,
140
- ) => {
140
+ ) : Promise < number | null > => {
141
141
const key = recycledNoncesKey ( chainId , walletAddress ) ;
142
- const res = await redis . spop ( key ) ;
143
- return res ? parseInt ( res ) : null ;
142
+ const result = await redis . zpopmin ( key ) ;
143
+ if ( result . length === 0 ) {
144
+ return null ;
145
+ }
146
+ return parseInt ( result [ 0 ] ) ;
144
147
} ;
145
148
146
149
const _syncNonce = async (
@@ -166,7 +169,7 @@ const _syncNonce = async (
166
169
/**
167
170
* Returns the last used nonce.
168
171
* This function should be used to inspect nonce values only.
169
- * Use `incrWalletNonce` if using this nonce to send a transaction.
172
+ * Use `acquireNonce` to fetch a nonce for sending a transaction.
170
173
* @param chainId
171
174
* @param walletAddress
172
175
* @returns number
@@ -178,7 +181,7 @@ export const inspectNonce = async (chainId: number, walletAddress: Address) => {
178
181
} ;
179
182
180
183
/**
181
- * Delete all wallet nonces. Useful when the get out of sync.
184
+ * Delete all wallet nonces. Useful when they get out of sync.
182
185
*/
183
186
export const deleteAllNonces = async ( ) => {
184
187
const keys = [
0 commit comments