Skip to content

Commit fb53e1e

Browse files
committed
feat: faucet txs now stacks mempool txs with retry-nonce-incrementing up to 5
1 parent 22f8e35 commit fb53e1e

File tree

1 file changed

+47
-20
lines changed

1 file changed

+47
-20
lines changed

src/api/routes/faucets.ts

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import { addAsync, RouterWithAsync } from '@awaitjs/express';
44
import * as btc from 'bitcoinjs-lib';
55
import PQueue from 'p-queue';
66
import * as BN from 'bn.js';
7-
import { makeSTXTokenTransfer, StacksNetwork } from '@blockstack/stacks-transactions';
7+
import {
8+
makeSTXTokenTransfer,
9+
SignedTokenTransferOptions,
10+
StacksNetwork,
11+
} from '@blockstack/stacks-transactions';
812
import { makeBtcFaucetPayment, getBtcBalance } from '../../btc-faucet';
913
import { DataStore, DbFaucetRequestCurrency } from '../../datastore/common';
10-
import { logger, stxToMicroStx } from '../../helpers';
14+
import { assertNotNullish as unwrap, logger, stxToMicroStx } from '../../helpers';
1115
import { testnetKeys, GetStacksTestnetNetwork } from './debug';
1216
import { StacksCoreRpcClient } from '../../core-rpc/client';
1317

@@ -88,6 +92,8 @@ export function createFaucetRouter(db: DataStore): RouterWithAsync {
8892
const FAUCET_STACKING_WINDOW = 2 * 24 * 60 * 60 * 1000; // 2 days
8993
const FAUCET_STACKING_TRIGGER_COUNT = 1;
9094

95+
const MAX_NONCE_INCREMENT_RETRIES = 5;
96+
9197
router.postAsync('/stx', async (req, res) => {
9298
await stxFaucetRequestQueue.add(async () => {
9399
const address: string = req.query.address || req.body.address;
@@ -120,30 +126,51 @@ export function createFaucetRouter(db: DataStore): RouterWithAsync {
120126
return;
121127
}
122128

123-
const tx = await makeSTXTokenTransfer({
124-
recipient: address,
125-
amount: new BN(stxAmount.toString()),
126-
senderKey: privateKey,
127-
network: getStxFaucetNetwork(),
128-
memo: 'Faucet',
129-
});
130-
129+
let nextNonce: BN | undefined = undefined;
130+
let sendError: Error | undefined = undefined;
131+
let sendSuccess = false;
132+
for (let i = 0; i < MAX_NONCE_INCREMENT_RETRIES; i++) {
133+
const txOpts: SignedTokenTransferOptions = {
134+
recipient: address,
135+
amount: new BN(stxAmount.toString()),
136+
senderKey: privateKey,
137+
network: getStxFaucetNetwork(),
138+
memo: 'Faucet',
139+
};
140+
if (nextNonce !== undefined) {
141+
txOpts.nonce = nextNonce;
142+
}
143+
const tx = await makeSTXTokenTransfer(txOpts);
144+
const serializedTx = tx.serialize();
145+
try {
146+
const txSendResult = await new StacksCoreRpcClient().sendTransaction(serializedTx);
147+
res.json({
148+
success: true,
149+
txId: txSendResult.txId,
150+
txRaw: tx.serialize().toString('hex'),
151+
});
152+
sendSuccess = true;
153+
break;
154+
} catch (error) {
155+
sendError = error;
156+
if (error.message?.includes('ConflictingNonceInMempool')) {
157+
nextNonce = unwrap(tx.auth.spendingCondition).nonce.add(new BN(1));
158+
} else if (error.message?.includes('BadNonce')) {
159+
nextNonce = undefined;
160+
} else {
161+
throw error;
162+
}
163+
}
164+
}
165+
if (!sendSuccess && sendError) {
166+
throw sendError;
167+
}
131168
await db.insertFaucetRequest({
132169
ip: `${ip}`,
133170
address: address,
134171
currency: DbFaucetRequestCurrency.STX,
135172
occurred_at: now,
136173
});
137-
138-
const hex = tx.serialize().toString('hex');
139-
const serializedTx = tx.serialize();
140-
const { txId } = await new StacksCoreRpcClient().sendTransaction(serializedTx);
141-
142-
res.json({
143-
success: true,
144-
txId,
145-
txRaw: hex,
146-
});
147174
});
148175
});
149176

0 commit comments

Comments
 (0)