@@ -42,6 +42,7 @@ export function getStxFaucetNetworks(): StacksNetwork[] {
42
42
enum TxSendResultStatus {
43
43
Success ,
44
44
ConflictingNonce ,
45
+ TooMuchChaining ,
45
46
Error ,
46
47
}
47
48
@@ -50,17 +51,12 @@ interface TxSendResultSuccess {
50
51
txId : string ;
51
52
}
52
53
53
- interface TxSendResultConflictingNonce {
54
- status : TxSendResultStatus . ConflictingNonce ;
55
- error : Error ;
56
- }
57
-
58
54
interface TxSendResultError {
59
- status : TxSendResultStatus . Error ;
55
+ status : TxSendResultStatus ;
60
56
error : Error ;
61
57
}
62
58
63
- type TxSendResult = TxSendResultSuccess | TxSendResultConflictingNonce | TxSendResultError ;
59
+ type TxSendResult = TxSendResultSuccess | TxSendResultError ;
64
60
65
61
function clientFromNetwork ( network : StacksNetwork ) : StacksCoreRpcClient {
66
62
const coreUrl = new URL ( network . coreApiUrl ) ;
@@ -148,6 +144,9 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
148
144
const FAUCET_STACKING_WINDOW = 2 * 24 * 60 * 60 * 1000 ; // 2 days
149
145
const FAUCET_STACKING_TRIGGER_COUNT = 1 ;
150
146
147
+ const STX_FAUCET_NETWORKS = getStxFaucetNetworks ( ) ;
148
+ const STX_FAUCET_KEYS = ( process . env . FAUCET_PRIVATE_KEY ?? testnetKeys [ 0 ] . secretKey ) . split ( ',' ) ;
149
+
151
150
router . post (
152
151
'/stx' ,
153
152
asyncHandler ( async ( req , res ) => {
@@ -167,8 +166,6 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
167
166
const ip = req . headers [ 'x-forwarded-for' ] || req . connection . remoteAddress ;
168
167
const lastRequests = await db . getSTXFaucetRequests ( address ) ;
169
168
170
- const privateKey = process . env . FAUCET_PRIVATE_KEY || testnetKeys [ 0 ] . secretKey ;
171
-
172
169
const isStackingReq = req . query [ 'stacking' ] === 'true' ;
173
170
174
171
// Guard condition: requests are limited to x times per y minutes.
@@ -191,10 +188,8 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
191
188
return ;
192
189
}
193
190
194
- const networks = getStxFaucetNetworks ( ) ;
195
-
196
191
const stxAmounts : bigint [ ] = [ ] ;
197
- for ( const network of networks ) {
192
+ for ( const network of STX_FAUCET_NETWORKS ) {
198
193
try {
199
194
let stxAmount = FAUCET_DEFAULT_STX_AMOUNT ;
200
195
if ( isStackingReq ) {
@@ -216,13 +211,14 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
216
211
217
212
const generateTx = async (
218
213
network : StacksNetwork ,
214
+ keyIndex : number ,
219
215
nonce ?: bigint ,
220
216
fee ?: bigint
221
217
) : Promise < StacksTransaction > => {
222
218
const txOpts : SignedTokenTransferOptions = {
223
219
recipient : address ,
224
220
amount : stxAmount ,
225
- senderKey : privateKey ,
221
+ senderKey : STX_FAUCET_KEYS [ keyIndex ] ,
226
222
network : network ,
227
223
memo : 'Faucet' ,
228
224
anchorMode : AnchorMode . Any ,
@@ -242,7 +238,7 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
242
238
/ e s t i m a t i n g t r a n s a c t i o n f e e | N o E s t i m a t e A v a i l a b l e / . test ( error . message )
243
239
) {
244
240
const defaultFee = 200n ;
245
- return await generateTx ( network , nonce , defaultFee ) ;
241
+ return await generateTx ( network , keyIndex , nonce , defaultFee ) ;
246
242
}
247
243
throw error ;
248
244
}
@@ -251,9 +247,9 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
251
247
const nonces : bigint [ ] = [ ] ;
252
248
const fees : bigint [ ] = [ ] ;
253
249
let txGenFetchError : Error | undefined ;
254
- for ( const network of networks ) {
250
+ for ( const network of STX_FAUCET_NETWORKS ) {
255
251
try {
256
- const tx = await generateTx ( network ) ;
252
+ const tx = await generateTx ( network , 0 ) ;
257
253
nonces . push ( tx . auth . spendingCondition ?. nonce ?? BigInt ( 0 ) ) ;
258
254
fees . push ( tx . auth . spendingCondition . fee ) ;
259
255
} catch ( error : any ) {
@@ -270,10 +266,11 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
270
266
let retrySend = false ;
271
267
let sendSuccess : { txId : string ; txRaw : string } | undefined ;
272
268
let lastSendError : Error | undefined ;
269
+ let stxKeyIndex = 0 ;
273
270
do {
274
- const tx = await generateTx ( networks [ 0 ] , nextNonce , fee ) ;
271
+ const tx = await generateTx ( STX_FAUCET_NETWORKS [ 0 ] , stxKeyIndex , nextNonce , fee ) ;
275
272
const rawTx = Buffer . from ( tx . serialize ( ) ) ;
276
- for ( const network of networks ) {
273
+ for ( const network of STX_FAUCET_NETWORKS ) {
277
274
const rpcClient = clientFromNetwork ( network ) ;
278
275
try {
279
276
const res = await rpcClient . sendTransaction ( rawTx ) ;
@@ -289,6 +286,11 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
289
286
status : TxSendResultStatus . ConflictingNonce ,
290
287
error,
291
288
} ) ;
289
+ } else if ( error . message ?. includes ( 'TooMuchChaining' ) ) {
290
+ sendTxResults . push ( {
291
+ status : TxSendResultStatus . TooMuchChaining ,
292
+ error,
293
+ } ) ;
292
294
} else {
293
295
sendTxResults . push ( {
294
296
status : TxSendResultStatus . Error ,
@@ -305,6 +307,16 @@ export function createFaucetRouter(db: PgWriteStore): express.Router {
305
307
retrySend = true ;
306
308
sendTxResults . length = 0 ;
307
309
nextNonce = nextNonce + 1n ;
310
+ } else if (
311
+ sendTxResults . every ( res => res . status === TxSendResultStatus . TooMuchChaining )
312
+ ) {
313
+ // Try with the next key in case we have one.
314
+ if ( stxKeyIndex + 1 === STX_FAUCET_KEYS . length ) {
315
+ retrySend = false ;
316
+ } else {
317
+ retrySend = true ;
318
+ stxKeyIndex ++ ;
319
+ }
308
320
} else {
309
321
retrySend = false ;
310
322
}
0 commit comments