diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..daa8da8 --- /dev/null +++ b/.env.example @@ -0,0 +1,29 @@ + +# production or development +NODE_ENV="production" +# local port the faucet will be hosted on +PORT=3000 +# testnet websocket address - Fill in other test networks url if needed +RIPPLED_URI="wss://s.altnet.rippletest.net:51233" +# funding address & secret, using genesis account is not recommended, please prefund your faucet account +FUNDING_ADDRESS= +FUNDING_SECRET= +# default faucet funding amount +XRP_AMOUNT= 1000 + +# Big Query credentials +BIGQUERY_PRIVATE_KEY="" +BIGQUERY_CLIENT_EMAIL="" +BIGQUERY_PROJECT_ID="" +BIGQUERY_DATASET_ID = "" +BIGQUERY_TABLE_ID = "" + +# Caspian credentials +CASPIAN_ENDPOINT="" +CASPIAN_API_KEY="" +CASPIAN_PRODUCER_NAME="" +CASPIAN_ENTITY_NAME="" +CASPIAN_SCHEMA_TYPE="" +CASPIAN_SCHEMA_VERSION= + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a06fff1..5b420a1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,14 +4,23 @@ Funds new Testnet accounts ## Usage -### Run the server: +### Run the server (example): +#### command line ``` npm install NODE_ENV="production" PORT=3000 RIPPLED_URI="wss://s.altnet.rippletest.net:51233" FUNDING_ADDRESS=rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe FUNDING_SECRET= XRP_AMOUNT=10000 npm start ``` +#### using env file +or fill out `.env` file using `.env.example` and run +``` +npm install +npm start + +``` + ### Fund a new account: ``` diff --git a/package-lock.json b/package-lock.json index c8b1656..f01d587 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "testnet-faucet", - "version": "1.0.0", + "version": "2.0.0", "license": "ISC", "dependencies": { "@google-cloud/bigquery": "^6.2.0", diff --git a/src/routes/accounts.ts b/src/routes/accounts.ts index 1bb3eff..bd2f0ef 100644 --- a/src/routes/accounts.ts +++ b/src/routes/accounts.ts @@ -69,12 +69,16 @@ export default async function (req: Request, res: Response) { payment.DestinationTag = account.tag; } + let transactionHash = fundingWallet.sign(payment).hash; try { let result; try { result = await submitPaymentWithTicket(payment, client, fundingWallet); + transactionHash = result.hash; } catch (err) { - console.log(`${rTracer.id()} | Failed to submit payment: ${err}`); + console.log( + `${rTracer.id()} | Failed to submit payment ${transactionHash}: ${err}` + ); res.status(500).send({ error: "Unable to fund account. Try again later", account, @@ -83,11 +87,11 @@ export default async function (req: Request, res: Response) { return; } - const status = result.engine_result; - + const status = result.result.engine_result; const response: FundedResponse = { account: account, amount: Number(amount), + transactionHash: transactionHash, }; if (wallet && wallet.seed) { @@ -98,7 +102,7 @@ export default async function (req: Request, res: Response) { console.log( `${rTracer.id()} | Funded ${ account.address - } with ${amount} XRP (${status})` + } with ${amount} XRP (${status}), paymentHash: ${transactionHash}` ); if (config.BIGQUERY_PROJECT_ID) { @@ -109,6 +113,7 @@ export default async function (req: Request, res: Response) { console.warn(`Failed to insert into BigQuery: ${error}`); } } + incrementTxCount(); res.send(response); } @@ -120,6 +125,7 @@ export default async function (req: Request, res: Response) { }); } } + async function insertIntoBigQuery( account: Account, amount: string, @@ -173,20 +179,31 @@ async function submitPaymentWithTicket( ) { let retryCount = 0; let result; + let hash; while (retryCount < maxRetries) { payment.TicketSequence = await getTicket(client); - result = (await client.submit(payment, { wallet: fundingWallet })).result; + payment = await client.autofill(payment); + const { tx_blob: paymentBlob, hash: paymentHash } = + fundingWallet.sign(payment); + hash = paymentHash; + result = (await client.submit(paymentBlob)).result; if (result.engine_result === "tefNO_TICKET") { retryCount++; - console.log(`Retrying transaction (${retryCount}/${maxRetries})`); - } else { + console.log(`Retrying transaction ${hash} (${retryCount}/${maxRetries})`); + } else if (result.engine_result === "tesSUCCESS") { break; + } else { + throw new Error( + `Failed to submit transaction ${hash} with ticket, error code: ${result.engine_result}` + ); } } if (retryCount >= maxRetries) { - throw new Error("Failed to submit transaction after multiple attempts"); + throw new Error( + `Failed to submit transaction ${hash} with ticket after multiple attempts` + ); } - return result; + return { result, hash }; } diff --git a/src/ticket-queue.ts b/src/ticket-queue.ts index af3bcca..29e2cf6 100644 --- a/src/ticket-queue.ts +++ b/src/ticket-queue.ts @@ -84,7 +84,9 @@ export async function getTicket(client: Client) { }) .catch((error) => { console.log( - `Failed to create tickets. Error: ${JSON.stringify(error)} Message: ${error?.message}` + `Failed to create tickets. Error: ${JSON.stringify(error)} Message: ${ + error?.message + }` ); }); } diff --git a/src/types.ts b/src/types.ts index 1a46699..1da8739 100644 --- a/src/types.ts +++ b/src/types.ts @@ -7,5 +7,6 @@ export interface Account { export interface FundedResponse { account: Account; amount: number; + transactionHash: string; seed?: string; }