Handle account sequence mismatch error #13

Merged
nabarun merged 3 commits from iv-chain-tx-error into main 2024-10-24 11:38:18 +00:00
7 changed files with 153 additions and 97 deletions
Showing only changes of commit a47dda4fbc - Show all commits

View File

@ -15,7 +15,7 @@ import {
ApplicationDeploymentRemovalRequest
} from './entity/Deployment';
import { AppDeploymentRecord, AppDeploymentRemovalRecord, AuctionParams, DeployerRecord } from './types';
import { getConfig, getRepoDetails, sleep } from './utils';
import { getConfig, getRepoDetails, registryTransactionWithRetry, sleep } from './utils';
const log = debug('snowball:registry');
@ -108,7 +108,8 @@ export class Registry {
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
const result = await this.registry.setRecord(
const result = await registryTransactionWithRetry(() =>
this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
record: applicationRecord,
@ -116,6 +117,7 @@ export class Registry {
},
this.registryConfig.privateKey,
fee
)
);
log(`Published application record ${result.id}`);
@ -126,33 +128,39 @@ export class Registry {
log(`Setting name: ${lrn} for record ID: ${result.id}`);
await sleep(SLEEP_DURATION);
await this.registry.setName(
await registryTransactionWithRetry(() =>
this.registry.setName(
{
cid: result.id,
lrn
},
this.registryConfig.privateKey,
fee
)
);
await sleep(SLEEP_DURATION);
await this.registry.setName(
await registryTransactionWithRetry(() =>
this.registry.setName(
{
cid: result.id,
lrn: `${lrn}@${applicationRecord.app_version}`
},
this.registryConfig.privateKey,
fee
)
);
await sleep(SLEEP_DURATION);
await this.registry.setName(
await registryTransactionWithRetry(() =>
this.registry.setName(
{
cid: result.id,
lrn: `${lrn}@${applicationRecord.repository_ref}`
},
this.registryConfig.privateKey,
fee
)
);
return {
@ -183,7 +191,8 @@ export class Registry {
const auctionConfig = config.auction;
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
const auctionResult = await this.registry.createProviderAuction(
const auctionResult = await registryTransactionWithRetry(() =>
this.registry.createProviderAuction(
{
commitFee: auctionConfig.commitFee,
commitsDuration: auctionConfig.commitsDuration,
@ -196,6 +205,7 @@ export class Registry {
this.registryConfig.privateKey,
fee
)
);
if (!auctionResult.auction) {
throw new Error('Error creating auction');
@ -208,7 +218,8 @@ export class Registry {
type: APP_DEPLOYMENT_AUCTION_RECORD_TYPE,
};
const result = await this.registry.setRecord(
const result = await registryTransactionWithRetry(() =>
this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
record: applicationDeploymentAuction,
@ -216,6 +227,7 @@ export class Registry {
},
this.registryConfig.privateKey,
fee
)
);
log(`Application deployment auction created: ${auctionResult.auction.id}`);
@ -274,7 +286,8 @@ export class Registry {
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
const result = await this.registry.setRecord(
const result = await registryTransactionWithRetry(() =>
this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
record: applicationDeploymentRequest,
@ -282,6 +295,7 @@ export class Registry {
},
this.registryConfig.privateKey,
fee
)
);
log(`Application deployment request record published: ${result.id}`);
@ -322,12 +336,14 @@ export class Registry {
auctionId: string
): Promise<any> {
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
const auction = await this.registry.releaseFunds(
const auction = await registryTransactionWithRetry(() =>
this.registry.releaseFunds(
{
auctionId
},
this.registryConfig.privateKey,
fee
)
);
return auction;
@ -424,7 +440,8 @@ export class Registry {
const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees);
const result = await this.registry.setRecord(
const result = await registryTransactionWithRetry(() =>
this.registry.setRecord(
{
privateKey: this.registryConfig.privateKey,
record: applicationDeploymentRemovalRequest,
@ -432,6 +449,7 @@ export class Registry {
},
this.registryConfig.privateKey,
fee
)
);
log(`Application deployment removal request record published: ${result.id}`);

View File

@ -120,3 +120,24 @@ export const getRepoDetails = async (
repoUrl
};
}
// Wrapper method for registry txs to retry once if 'account sequence mismatch' occurs
export const registryTransactionWithRetry = async (
txMethod: () => Promise<any>
): Promise<any> => {
try {
return await txMethod();
} catch (error: any) {
if (!error.message.includes('account sequence mismatch')) {
throw error;
}
console.error(`Transaction failed due to account sequence mismatch. Retrying...`);
try {
return await txMethod();
} catch (retryError: any) {
throw new Error(`Transaction failed again after retry: ${retryError.message}`);
}
}
}

View File

@ -1,7 +1,10 @@
import ConfirmDialog, {
ConfirmDialogProps,
} from 'components/shared/ConfirmDialog';
import { ArrowRightCircleFilledIcon, LoadingIcon } from 'components/shared/CustomIcon';
import {
ArrowRightCircleFilledIcon,
LoadingIcon,
} from 'components/shared/CustomIcon';
interface DeleteDeploymentDialogProps extends ConfirmDialogProps {
isConfirmButtonLoading?: boolean;
@ -20,7 +23,11 @@ export const DeleteDeploymentDialog = ({
dialogTitle="Delete deployment?"
handleCancel={handleCancel}
open={open}
confirmButtonTitle={isConfirmButtonLoading ? "Deleting deployment" : "Yes, delete deployment"}
confirmButtonTitle={
isConfirmButtonLoading
? 'Deleting deployment'
: 'Yes, delete deployment'
}
handleConfirm={handleConfirm}
confirmButtonProps={{
variant: 'danger',

View File

@ -3,7 +3,11 @@ import { useForm, Controller } from 'react-hook-form';
import { FormProvider, FieldValues } from 'react-hook-form';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useMediaQuery } from 'usehooks-ts';
import { AddEnvironmentVariableInput, AuctionParams, Deployer } from 'gql-client';
import {
AddEnvironmentVariableInput,
AuctionParams,
Deployer,
} from 'gql-client';
import { Select, MenuItem, FormControl, FormHelperText } from '@mui/material';
@ -175,13 +179,13 @@ const Configure = () => {
);
const fetchDeployers = useCallback(async () => {
const res = await client.getDeployers()
setDeployers(res.deployers)
}, [client])
const res = await client.getDeployers();
setDeployers(res.deployers);
}, [client]);
useEffect(() => {
fetchDeployers()
}, [])
fetchDeployers();
}, []);
return (
<div className="space-y-7 px-4 py-6">
@ -209,7 +213,7 @@ const Configure = () => {
<Select
value={value}
onChange={(event) => onChange(event.target.value)}
size='small'
size="small"
displayEmpty
>
<MenuItem value="LRN">Deployer LRN</MenuItem>
@ -240,15 +244,22 @@ const Configure = () => {
value={value}
onChange={(event) => onChange(event.target.value)}
displayEmpty
size='small'
size="small"
>
{deployers.map((deployer) => (
<MenuItem key={deployer.deployerLrn} value={deployer.deployerLrn}>
<MenuItem
key={deployer.deployerLrn}
value={deployer.deployerLrn}
>
{deployer.deployerLrn}
</MenuItem>
))}
</Select>
{fieldState.error && <FormHelperText>{fieldState.error.message}</FormHelperText>}
{fieldState.error && (
<FormHelperText>
{fieldState.error.message}
</FormHelperText>
)}
</FormControl>
)}
/>

View File

@ -105,7 +105,6 @@ export const AuctionCard = ({ project }: { project: Project }) => {
</span>
</div>
<div className="flex justify-between items-center mt-1">
<span className="text-elements-high-em text-sm font-medium tracking-tight">
Auction Status

View File

@ -132,7 +132,7 @@ export const project: Project = {
deployerApiUrl: 'https://webapp-deployer-api.example.com',
deployerId: 'bafyreicrtgmkir4evvvysxdqxddf2ftdq2wrzuodgvwnxr4rmubi4obdfu',
deployerLrn: 'lrn://deployer.apps.snowballtools.com ',
}
},
],
webhooks: ['beepboop'],
icon: 'Icon',