Fix: add retry mechanism in e2e account manager (#497)

* fix: add retry mechanism in e2e account manager

* Update e2e/src/config/tests-config/accounts/account-manager.ts

Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com>
Signed-off-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>

* Update e2e/src/config/tests-config/accounts/account-manager.ts

Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com>
Signed-off-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>

* fix: update account manager logs

---------

Signed-off-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com>
This commit is contained in:
Victorien Gauch
2025-01-08 18:25:18 +01:00
committed by GitHub
parent 03b7667376
commit b194b90bb6

View File

@@ -33,6 +33,9 @@ abstract class AccountManager implements IAccountManager {
private whaleAccountMutex: Mutex;
private logger: Logger;
private readonly MAX_RETRIES = 5;
private readonly RETRY_DELAY_MS = 1_000;
constructor(provider: Provider, whaleAccounts: Account[], chainId: number) {
this.provider = provider;
this.whaleAccounts = whaleAccounts;
@@ -75,7 +78,7 @@ abstract class AccountManager implements IAccountManager {
);
const accounts: Account[] = [];
const transactionResponses: TransactionResponse[] = [];
const transactionPromises: Promise<TransactionResponse>[] = [];
for (let i = 0; i < numberOfAccounts; i++) {
const randomBytes = ethers.randomBytes(32);
@@ -90,26 +93,59 @@ abstract class AccountManager implements IAccountManager {
gasLimit: 21000n,
};
const release = await this.whaleAccountMutex.acquire();
try {
const transactionResponse = await whaleAccountWallet.sendTransaction(tx);
this.logger.debug(
`Transaction sent. newAccount=${newAccount.address} txHash=${transactionResponse.hash} whaleAccount=${whaleAccount.address}`,
const sendTransactionWithRetry = async (): Promise<TransactionResponse> => {
return this.retry<TransactionResponse>(
async () => {
const release = await this.whaleAccountMutex.acquire();
try {
const transactionResponse = await whaleAccountWallet.sendTransaction(tx);
this.logger.debug(
`Transaction sent. newAccount=${newAccount.address} txHash=${transactionResponse.hash} whaleAccount=${whaleAccount.address}`,
);
return transactionResponse;
} catch (error) {
this.logger.warn(
`sendTransaction failed for account=${newAccount.address}. Error: ${(error as Error).message}`,
);
throw error;
} finally {
release();
}
},
this.MAX_RETRIES,
this.RETRY_DELAY_MS,
);
};
const txPromise = sendTransactionWithRetry().catch((error) => {
this.logger.error(
`Failed to fund account after ${this.MAX_RETRIES} attempts. address=${newAccount.address} error=${error.message}`,
);
transactionResponses.push(transactionResponse);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
logger.error(`Failed to fund account. address=${newAccount.address} error=${error.message}`);
whaleAccountWallet.reset();
} finally {
release();
}
return null as unknown as TransactionResponse;
});
transactionPromises.push(txPromise);
}
await Promise.all(transactionResponses.map((tx) => tx.wait()));
const transactionResponses = await Promise.all(transactionPromises);
const successfulTransactions = transactionResponses.filter(
(txResponse): txResponse is TransactionResponse => txResponse !== null,
);
if (successfulTransactions.length < numberOfAccounts) {
this.logger.warn(
`Some accounts were not funded successfully. successful=${successfulTransactions.length} expected=${numberOfAccounts}`,
);
}
await Promise.all(successfulTransactions.map((tx) => tx.wait()));
this.logger.debug(
`Accounts funded. newAccounts=${accounts.map((account) => account.address).join(",")} balance=${initialBalanceWei.toString()} wei`,
`${successfulTransactions.length} accounts funded. newAccounts=${accounts
.map((account) => account.address)
.join(", ")} balance=${initialBalanceWei.toString()} Wei`,
);
return accounts.map((account) => this.getWallet(account));
@@ -118,6 +154,30 @@ abstract class AccountManager implements IAccountManager {
getWallet(account: Account): Wallet {
return getWallet(this.provider, account.privateKey);
}
private async retry<T>(fn: () => Promise<T>, retries: number, delayMs: number): Promise<T> {
let attempt = 0;
while (attempt < retries) {
try {
return await fn();
} catch (error) {
attempt++;
if (attempt >= retries) {
this.logger.error(`Operation failed after attempts=${attempt} error=${(error as Error).message}`);
throw error;
}
this.logger.warn(`Attempt ${attempt} failed. Retrying in ${delayMs}ms... error=${(error as Error).message}`);
await this.delay(delayMs);
}
}
throw new Error("Unexpected error in retry mechanism.");
}
private delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
export { AccountManager };