From f3fd992847d1f89127df333b145975855bb7e7f8 Mon Sep 17 00:00:00 2001 From: Jonas Hahn Date: Fri, 24 Jan 2025 15:35:33 +0100 Subject: [PATCH 1/4] Improve transaction sending helpers - Added a polling to sendTransactionWithRetry - Fixed missing confirmation status in simulateForCompute - Increased default priority fee to 10000 --- src/lib/transaction.ts | 67 +++++++++++++++++++++-------------- tests/src/transaction.test.ts | 11 +++--- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/lib/transaction.ts b/src/lib/transaction.ts index ce4846f..e4fa8b4 100644 --- a/src/lib/transaction.ts +++ b/src/lib/transaction.ts @@ -30,7 +30,7 @@ import * as path from "path"; export const confirmTransaction = async ( connection: Connection, signature: string, - commitment: Commitment = "finalized", + commitment: Commitment = "confirmed", ): Promise => { const block = await connection.getLatestBlockhash(); const rpcResponse = await connection.confirmTransaction( @@ -54,6 +54,7 @@ export const getSimulationComputeUnits = async ( instructions: Array, payer: PublicKey, lookupTables: Array | [], + commitment: Commitment = "confirmed", ): Promise => { const testInstructions = [ // Set an arbitrarily high number in simulation @@ -76,6 +77,7 @@ export const getSimulationComputeUnits = async ( const rpcResponse = await connection.simulateTransaction(testTransaction, { replaceRecentBlockhash: true, sigVerify: false, + commitment, }); if (rpcResponse?.value?.err) { @@ -90,7 +92,8 @@ export const getSimulationComputeUnits = async ( * Constants for transaction retry configuration */ export const RETRY_INTERVAL_MS = 2000; -export const MAX_RETRIES = 30; +export const RETRY_INTERVAL_INCREASE = 200; +export const MAX_RETRIES = 15; /** * Represents the different states of a transaction during its lifecycle @@ -102,6 +105,7 @@ export type TxStatusUpdate = | { status: "created" } | { status: "signed" } | { status: "sent"; signature: string } + | { status: "retry"; signature: string | null } | { status: "confirmed"; result: SignatureStatus }; /** @@ -193,7 +197,7 @@ export async function sendTransactionWithRetry( initialDelayMs = DEFAULT_SEND_OPTIONS.initialDelayMs, commitment = DEFAULT_SEND_OPTIONS.commitment, skipPreflight = DEFAULT_SEND_OPTIONS.skipPreflight, - onStatusUpdate = () => {}, + onStatusUpdate = (status) => console.log("Transaction status:", status), }: SendTransactionOptions = {}, ): Promise { onStatusUpdate?.({ status: "created" }); @@ -208,34 +212,41 @@ export async function sendTransactionWithRetry( let signature: string | null = null; let status: SignatureStatus | null = null; let retries = 0; + // Setting a minimum to decrease spam and for the confirmation to work + let delayBetweenRetries = Math.max(initialDelayMs, 500); while (retries < maxRetries) { try { + const isFirstSend = signature === null; + // Send transaction if not sent yet - if (!signature) { - signature = await connection.sendRawTransaction( - transaction.serialize(), - { - skipPreflight, - preflightCommitment: commitment, - maxRetries: 0, - }, - ); + signature = await connection.sendRawTransaction(transaction.serialize(), { + skipPreflight, + preflightCommitment: commitment, + maxRetries: 0, + }); + + if (isFirstSend) { onStatusUpdate?.({ status: "sent", signature }); } - // Check status - const response = await connection.getSignatureStatus(signature); - if (response?.value) { - status = response.value; - - if ( - status.confirmationStatus === "confirmed" || - status.confirmationStatus === "finalized" - ) { - onStatusUpdate?.({ status: "confirmed", result: status }); - return signature; + // Poll for confirmation + let pollTimeout = delayBetweenRetries; + const timeBetweenPolls = 500; + while (pollTimeout > 0) { + await new Promise((resolve) => setTimeout(resolve, timeBetweenPolls)); + const response = await connection.getSignatureStatus(signature); + if (response?.value) { + status = response.value; + if ( + status.confirmationStatus === "confirmed" || + status.confirmationStatus === "finalized" + ) { + onStatusUpdate?.({ status: "confirmed", result: status }); + return signature; + } } + pollTimeout -= timeBetweenPolls; } } catch (error: unknown) { if (error instanceof Error) { @@ -246,8 +257,10 @@ export async function sendTransactionWithRetry( } retries++; + if (retries < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, initialDelayMs)); + onStatusUpdate?.({ status: "retry", signature: signature ?? null }); + delayBetweenRetries += RETRY_INTERVAL_INCREASE; } } @@ -260,7 +273,7 @@ export async function sendTransactionWithRetry( * @param connection - The Solana connection object * @param tx - The transaction to prepare * @param payer - The public key of the transaction payer - * @param priorityFee - Priority fee in microLamports (default: 1000) + * @param priorityFee - Priority fee in microLamports (default: 10000 which is the minimum required for helius to see a transaction as priority) * @param computeUnitBuffer - Optional buffer to add to simulated compute units * * @remarks @@ -299,8 +312,9 @@ export async function prepareTransactionWithCompute( connection: Connection, tx: Transaction, payer: PublicKey, - priorityFee: number = 1000, + priorityFee: number = 10000, computeUnitBuffer: ComputeUnitBuffer = {}, + commitment: Commitment = "confirmed", ): Promise { tx.add( ComputeBudgetProgram.setComputeUnitPrice({ @@ -313,6 +327,7 @@ export async function prepareTransactionWithCompute( tx.instructions, payer, [], + commitment, ); if (simulatedCompute === null) { diff --git a/tests/src/transaction.test.ts b/tests/src/transaction.test.ts index ab292c4..d553a09 100644 --- a/tests/src/transaction.test.ts +++ b/tests/src/transaction.test.ts @@ -99,7 +99,7 @@ describe("getSimulationComputeUnits", () => { }); describe("Transaction utilities", () => { - test.only("sendTransactionWithRetry should send and confirm a transaction", async () => { + test("sendTransactionWithRetry should send and confirm a transaction", async () => { const connection = new Connection(LOCALHOST); const sender = Keypair.generate(); await airdropIfRequired( @@ -129,8 +129,10 @@ describe("Transaction utilities", () => { transaction, [sender], { - commitment: "confirmed", - onStatusUpdate: (status) => statusUpdates.push(status), + onStatusUpdate: (status) => { + statusUpdates.push(status); + console.log("status", status); + }, }, ); @@ -141,9 +143,10 @@ describe("Transaction utilities", () => { ); }); - test.only("prepareTransactionWithCompute should add compute budget instructions", async () => { + test("prepareTransactionWithCompute should add compute budget instructions", async () => { const connection = new Connection(LOCALHOST); const sender = Keypair.generate(); + await airdropIfRequired( connection, sender.publicKey, From 042ad53c7a433b5a3c27fd94cadf9910944d2f02 Mon Sep 17 00:00:00 2001 From: Jonas Hahn Date: Fri, 24 Jan 2025 16:02:02 +0100 Subject: [PATCH 2/4] Remove createAccountsMintsAndTokenAccounts --- CHANGELOG.md | 5 +++ README.md | 35 --------------- src/lib/token.ts | 97 ----------------------------------------- tests/src/token.test.ts | 61 +++----------------------- 4 files changed, 10 insertions(+), 188 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e6ff1..ed4eba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.6 + +- Added Transaction send helpers. `prepareTransactionWithCompute()` and `sendTransactionWithRetry()` +- Removed `createAccountsMintsAndTokenAccounts()` because its not a useful helper function and does not work + ## 2.5 - Add `makeTokenMint()` diff --git a/README.md b/README.md index 217c12f..6d0f2ae 100644 --- a/README.md +++ b/README.md @@ -96,41 +96,6 @@ const mintAddress = await makeTokenMint( ); ``` -### Create users, mints and token accounts in a single step - -Frequently, tests for onchain programs need to make not just users with SOL, but also token mints and give each user some balance of each token. To save this boilerplate, `createAccountsMintsAndTokenAccounts()` handles making user keypairs, giving them SOL, making mints, creating associated token accounts, and minting tokens directly to the associated token accounts. - -Eg, to make two new users, and two tokens: - -- the first user with million of the first token, none of the second token, and 1 SOL -- the second user with none of the first token, 1 million of the second token, and 1 SOL - -Just run: - -```typescript -const usersMintsAndTokenAccounts = await createAccountsMintsAndTokenAccounts( - [ - [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B - [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B - ], - 1 * LAMPORTS_PER_SOL, - connection, - payer, -); -``` - -The returned `usersMintsAndTokenAccounts` will be an object of the form: - -``` -{ - users: > - mints: >, - tokenAccounts: >> -} -``` - -tokenAccounts are indexed by the user, then the mint. Eg, the ATA of `user[0]` for `mint[0]` is `tokenAccounts[0][0]`. - ### Resolve a custom error message Usage: diff --git a/src/lib/token.ts b/src/lib/token.ts index cfd8e3f..c34e5f8 100644 --- a/src/lib/token.ts +++ b/src/lib/token.ts @@ -7,103 +7,6 @@ import { createUpdateFieldInstruction, pack, TokenMetadata } from "@solana/spl-t const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID; -// Create users, mints, create ATAs and mint tokens. -// TODO: we may actually want to split this into multiple transactions -// to avoid the transaction size limit (or use lookup tables) -// in the future. However it works for two transactions of the size -// used in our unit tests. -export const createAccountsMintsAndTokenAccounts = async ( - usersAndTokenBalances: Array>, - lamports: number, - connection: Connection, - payer: Keypair, -) => { - const userCount = usersAndTokenBalances.length; - // Set the variable mintCount to the largest array in the usersAndTokenBalances array - const mintCount = Math.max( - ...usersAndTokenBalances.map((mintBalances) => mintBalances.length), - ); - - const users = makeKeypairs(userCount); - const mints = makeKeypairs(mintCount); - - // This will be returned - // [user index][mint index]address of token account - let tokenAccounts: Array>; - - tokenAccounts = users.map((user) => { - return mints.map((mint) => - getAssociatedTokenAddressSync( - mint.publicKey, - user.publicKey, - false, - TOKEN_PROGRAM, - ), - ); - }); - - const sendSolInstructions: Array = users.map((user) => - SystemProgram.transfer({ - fromPubkey: payer.publicKey, - toPubkey: user.publicKey, - lamports, - }), - ); - - // Airdrops to user - const minimumLamports = await getMinimumBalanceForRentExemptMint(connection); - - const createMintInstructions: Array = mints.map( - (mint) => - SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: mint.publicKey, - lamports: minimumLamports, - space: MINT_SIZE, - programId: TOKEN_PROGRAM, - }), - ); - - // Make tokenA and tokenB mints, mint tokens and create ATAs - const mintTokensInstructions: Array = - usersAndTokenBalances.flatMap((userTokenBalances, userIndex) => { - return userTokenBalances.flatMap((tokenBalance, mintIndex) => { - if (tokenBalance === 0) { - return []; - } - return makeMintInstructions( - mints[mintIndex].publicKey, - tokenAccounts[userIndex][mintIndex], - tokenBalance, - users[userIndex].publicKey, - payer.publicKey, - ); - }); - }); - - const instructions = [ - ...sendSolInstructions, - ...createMintInstructions, - ...mintTokensInstructions, - ]; - - const signers = [...users, ...mints, payer]; - - // Finally, make the transaction and send it. - await makeAndSendAndConfirmTransaction( - connection, - instructions, - signers, - payer, - ); - - return { - users, - mints, - tokenAccounts, - }; -}; - export const makeTokenMint = async ( connection: Connection, mintAuthority: Keypair, diff --git a/tests/src/token.test.ts b/tests/src/token.test.ts index 6e3dfe6..0f9f4d5 100644 --- a/tests/src/token.test.ts +++ b/tests/src/token.test.ts @@ -1,10 +1,13 @@ import { describe, test } from "node:test"; import { LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { airdropIfRequired, createAccountsMintsAndTokenAccounts, makeTokenMint } from "../../src"; +import { + airdropIfRequired, + makeTokenMint, +} from "../../src"; import { Connection } from "@solana/web3.js"; import { Keypair } from "@solana/web3.js"; import { getTokenMetadata } from "@solana/spl-token"; -import assert from 'node:assert'; +import assert from "node:assert"; const LOCALHOST = "http://127.0.0.1:8899"; @@ -69,57 +72,3 @@ describe("makeTokenMint", () => { }); }); -describe("createAccountsMintsAndTokenAccounts", () => { - test("createAccountsMintsAndTokenAccounts works", async () => { - const payer = Keypair.generate(); - const connection = new Connection(LOCALHOST); - await airdropIfRequired( - connection, - payer.publicKey, - 100 * LAMPORTS_PER_SOL, - 1 * LAMPORTS_PER_SOL, - ); - - const SOL_BALANCE = 10 * LAMPORTS_PER_SOL; - - const usersMintsAndTokenAccounts = - await createAccountsMintsAndTokenAccounts( - [ - [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B - [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B - ], - SOL_BALANCE, - connection, - payer, - ); - - // Check all users have been created and have some SOL - const users = usersMintsAndTokenAccounts.users; - assert.equal(users.length, 2); - await Promise.all( - users.map(async (user) => { - const balance = await connection.getBalance(user.publicKey); - assert(balance === SOL_BALANCE); - }), - ); - - // Check the mints - assert.equal(usersMintsAndTokenAccounts.mints.length, 2); - - // Check the token accounts - const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts; - - // Get the balances of the token accounts for the first user - // (note there is no tokenAccountB balance yet) - const firstUserFirstTokenBalance = await connection.getTokenAccountBalance( - tokenAccounts[0][0], // First user, first token mint - ); - assert(Number(firstUserFirstTokenBalance.value.amount) === 1_000_000_000); - - // // Get the balances of the token accounts for the second user - // // (note there is no tokenAccountA account yet) - const secondUserSecondTokenBalance = - await connection.getTokenAccountBalance(tokenAccounts[1][1]); // Second user, second token mint - assert(Number(secondUserSecondTokenBalance.value.amount) === 1_000_000_000); - }); -}); \ No newline at end of file From 22a3996be50171a94db07c2e275b3d43d6014294 Mon Sep 17 00:00:00 2001 From: Jonas Hahn Date: Fri, 24 Jan 2025 16:05:04 +0100 Subject: [PATCH 3/4] Revert "Remove createAccountsMintsAndTokenAccounts" This reverts commit 042ad53c7a433b5a3c27fd94cadf9910944d2f02. --- CHANGELOG.md | 5 --- README.md | 35 +++++++++++++++ src/lib/token.ts | 97 +++++++++++++++++++++++++++++++++++++++++ tests/src/token.test.ts | 61 +++++++++++++++++++++++--- 4 files changed, 188 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4eba4..51e6ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,3 @@ -## 2.6 - -- Added Transaction send helpers. `prepareTransactionWithCompute()` and `sendTransactionWithRetry()` -- Removed `createAccountsMintsAndTokenAccounts()` because its not a useful helper function and does not work - ## 2.5 - Add `makeTokenMint()` diff --git a/README.md b/README.md index 6d0f2ae..217c12f 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,41 @@ const mintAddress = await makeTokenMint( ); ``` +### Create users, mints and token accounts in a single step + +Frequently, tests for onchain programs need to make not just users with SOL, but also token mints and give each user some balance of each token. To save this boilerplate, `createAccountsMintsAndTokenAccounts()` handles making user keypairs, giving them SOL, making mints, creating associated token accounts, and minting tokens directly to the associated token accounts. + +Eg, to make two new users, and two tokens: + +- the first user with million of the first token, none of the second token, and 1 SOL +- the second user with none of the first token, 1 million of the second token, and 1 SOL + +Just run: + +```typescript +const usersMintsAndTokenAccounts = await createAccountsMintsAndTokenAccounts( + [ + [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B + [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B + ], + 1 * LAMPORTS_PER_SOL, + connection, + payer, +); +``` + +The returned `usersMintsAndTokenAccounts` will be an object of the form: + +``` +{ + users: > + mints: >, + tokenAccounts: >> +} +``` + +tokenAccounts are indexed by the user, then the mint. Eg, the ATA of `user[0]` for `mint[0]` is `tokenAccounts[0][0]`. + ### Resolve a custom error message Usage: diff --git a/src/lib/token.ts b/src/lib/token.ts index c34e5f8..cfd8e3f 100644 --- a/src/lib/token.ts +++ b/src/lib/token.ts @@ -7,6 +7,103 @@ import { createUpdateFieldInstruction, pack, TokenMetadata } from "@solana/spl-t const TOKEN_PROGRAM: typeof TOKEN_2022_PROGRAM_ID | typeof TOKEN_PROGRAM_ID = TOKEN_2022_PROGRAM_ID; +// Create users, mints, create ATAs and mint tokens. +// TODO: we may actually want to split this into multiple transactions +// to avoid the transaction size limit (or use lookup tables) +// in the future. However it works for two transactions of the size +// used in our unit tests. +export const createAccountsMintsAndTokenAccounts = async ( + usersAndTokenBalances: Array>, + lamports: number, + connection: Connection, + payer: Keypair, +) => { + const userCount = usersAndTokenBalances.length; + // Set the variable mintCount to the largest array in the usersAndTokenBalances array + const mintCount = Math.max( + ...usersAndTokenBalances.map((mintBalances) => mintBalances.length), + ); + + const users = makeKeypairs(userCount); + const mints = makeKeypairs(mintCount); + + // This will be returned + // [user index][mint index]address of token account + let tokenAccounts: Array>; + + tokenAccounts = users.map((user) => { + return mints.map((mint) => + getAssociatedTokenAddressSync( + mint.publicKey, + user.publicKey, + false, + TOKEN_PROGRAM, + ), + ); + }); + + const sendSolInstructions: Array = users.map((user) => + SystemProgram.transfer({ + fromPubkey: payer.publicKey, + toPubkey: user.publicKey, + lamports, + }), + ); + + // Airdrops to user + const minimumLamports = await getMinimumBalanceForRentExemptMint(connection); + + const createMintInstructions: Array = mints.map( + (mint) => + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint.publicKey, + lamports: minimumLamports, + space: MINT_SIZE, + programId: TOKEN_PROGRAM, + }), + ); + + // Make tokenA and tokenB mints, mint tokens and create ATAs + const mintTokensInstructions: Array = + usersAndTokenBalances.flatMap((userTokenBalances, userIndex) => { + return userTokenBalances.flatMap((tokenBalance, mintIndex) => { + if (tokenBalance === 0) { + return []; + } + return makeMintInstructions( + mints[mintIndex].publicKey, + tokenAccounts[userIndex][mintIndex], + tokenBalance, + users[userIndex].publicKey, + payer.publicKey, + ); + }); + }); + + const instructions = [ + ...sendSolInstructions, + ...createMintInstructions, + ...mintTokensInstructions, + ]; + + const signers = [...users, ...mints, payer]; + + // Finally, make the transaction and send it. + await makeAndSendAndConfirmTransaction( + connection, + instructions, + signers, + payer, + ); + + return { + users, + mints, + tokenAccounts, + }; +}; + export const makeTokenMint = async ( connection: Connection, mintAuthority: Keypair, diff --git a/tests/src/token.test.ts b/tests/src/token.test.ts index 0f9f4d5..6e3dfe6 100644 --- a/tests/src/token.test.ts +++ b/tests/src/token.test.ts @@ -1,13 +1,10 @@ import { describe, test } from "node:test"; import { LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { - airdropIfRequired, - makeTokenMint, -} from "../../src"; +import { airdropIfRequired, createAccountsMintsAndTokenAccounts, makeTokenMint } from "../../src"; import { Connection } from "@solana/web3.js"; import { Keypair } from "@solana/web3.js"; import { getTokenMetadata } from "@solana/spl-token"; -import assert from "node:assert"; +import assert from 'node:assert'; const LOCALHOST = "http://127.0.0.1:8899"; @@ -72,3 +69,57 @@ describe("makeTokenMint", () => { }); }); +describe("createAccountsMintsAndTokenAccounts", () => { + test("createAccountsMintsAndTokenAccounts works", async () => { + const payer = Keypair.generate(); + const connection = new Connection(LOCALHOST); + await airdropIfRequired( + connection, + payer.publicKey, + 100 * LAMPORTS_PER_SOL, + 1 * LAMPORTS_PER_SOL, + ); + + const SOL_BALANCE = 10 * LAMPORTS_PER_SOL; + + const usersMintsAndTokenAccounts = + await createAccountsMintsAndTokenAccounts( + [ + [1_000_000_000, 0], // User 0 has 1_000_000_000 of token A and 0 of token B + [0, 1_000_000_000], // User 1 has 0 of token A and 1_000_000_000 of token B + ], + SOL_BALANCE, + connection, + payer, + ); + + // Check all users have been created and have some SOL + const users = usersMintsAndTokenAccounts.users; + assert.equal(users.length, 2); + await Promise.all( + users.map(async (user) => { + const balance = await connection.getBalance(user.publicKey); + assert(balance === SOL_BALANCE); + }), + ); + + // Check the mints + assert.equal(usersMintsAndTokenAccounts.mints.length, 2); + + // Check the token accounts + const tokenAccounts = usersMintsAndTokenAccounts.tokenAccounts; + + // Get the balances of the token accounts for the first user + // (note there is no tokenAccountB balance yet) + const firstUserFirstTokenBalance = await connection.getTokenAccountBalance( + tokenAccounts[0][0], // First user, first token mint + ); + assert(Number(firstUserFirstTokenBalance.value.amount) === 1_000_000_000); + + // // Get the balances of the token accounts for the second user + // // (note there is no tokenAccountA account yet) + const secondUserSecondTokenBalance = + await connection.getTokenAccountBalance(tokenAccounts[1][1]); // Second user, second token mint + assert(Number(secondUserSecondTokenBalance.value.amount) === 1_000_000_000); + }); +}); \ No newline at end of file From 22a83614d067d76d3603052a56073e8e9e2bb7dd Mon Sep 17 00:00:00 2001 From: Jonas Hahn Date: Fri, 24 Jan 2025 16:34:33 +0100 Subject: [PATCH 4/4] Fix confirmTransaction function --- CHANGELOG.md | 7 +++++++ src/lib/token.ts | 5 ++--- src/lib/transaction.ts | 4 ++-- tests/src/token.test.ts | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51e6ff1..9f18f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.6 + +- Added Transaction send helpers. `prepareTransactionWithCompute()` and `sendTransactionWithRetry()` +- Added Transaction Parser helper functions `getIdlParsedAccountData()`, `parseAnchorTransactionEvents()` and `getIdlParsedInstructionData()` +- Fixed `createAccountsMintsAndTokenAccounts()` function to use correct commitment and not `max` blockhash +- Fixed `confirmTransaction()` to not use correct commitment + ## 2.5 - Add `makeTokenMint()` diff --git a/src/lib/token.ts b/src/lib/token.ts index cfd8e3f..6a683a6 100644 --- a/src/lib/token.ts +++ b/src/lib/token.ts @@ -250,12 +250,11 @@ const makeAndSendAndConfirmTransaction = async ( signers: Array, payer: Keypair, ) => { - const latestBlockhash = (await connection.getLatestBlockhash("max")) - .blockhash; + const latestBlockhash = await connection.getLatestBlockhash("confirmed"); const messageV0 = new TransactionMessage({ payerKey: payer.publicKey, - recentBlockhash: latestBlockhash, + recentBlockhash: latestBlockhash.blockhash, instructions, }).compileToV0Message(); const transaction = new VersionedTransaction(messageV0); diff --git a/src/lib/transaction.ts b/src/lib/transaction.ts index e4fa8b4..cc648ed 100644 --- a/src/lib/transaction.ts +++ b/src/lib/transaction.ts @@ -32,7 +32,7 @@ export const confirmTransaction = async ( signature: string, commitment: Commitment = "confirmed", ): Promise => { - const block = await connection.getLatestBlockhash(); + const block = await connection.getLatestBlockhash(commitment); const rpcResponse = await connection.confirmTransaction( { signature, @@ -213,7 +213,7 @@ export async function sendTransactionWithRetry( let status: SignatureStatus | null = null; let retries = 0; // Setting a minimum to decrease spam and for the confirmation to work - let delayBetweenRetries = Math.max(initialDelayMs, 500); + let delayBetweenRetries = Math.max(initialDelayMs, 500); while (retries < maxRetries) { try { diff --git a/tests/src/token.test.ts b/tests/src/token.test.ts index 6e3dfe6..07f6de6 100644 --- a/tests/src/token.test.ts +++ b/tests/src/token.test.ts @@ -72,7 +72,7 @@ describe("makeTokenMint", () => { describe("createAccountsMintsAndTokenAccounts", () => { test("createAccountsMintsAndTokenAccounts works", async () => { const payer = Keypair.generate(); - const connection = new Connection(LOCALHOST); + const connection = new Connection(LOCALHOST, "confirmed"); await airdropIfRequired( connection, payer.publicKey,