diff --git a/packages/backend/src/registry.ts b/packages/backend/src/registry.ts index 9371baaa..59f3a178 100644 --- a/packages/backend/src/registry.ts +++ b/packages/backend/src/registry.ts @@ -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,14 +108,16 @@ export class Registry { const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees); - const result = await this.registry.setRecord( - { - privateKey: this.registryConfig.privateKey, - record: applicationRecord, - bondId: this.registryConfig.bondId - }, - this.registryConfig.privateKey, - fee + const result = await registryTransactionWithRetry(() => + this.registry.setRecord( + { + privateKey: this.registryConfig.privateKey, + record: applicationRecord, + bondId: this.registryConfig.bondId + }, + 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( - { - cid: result.id, - lrn - }, - this.registryConfig.privateKey, - fee + await registryTransactionWithRetry(() => + this.registry.setName( + { + cid: result.id, + lrn + }, + this.registryConfig.privateKey, + fee + ) ); await sleep(SLEEP_DURATION); - await this.registry.setName( - { - cid: result.id, - lrn: `${lrn}@${applicationRecord.app_version}` - }, - this.registryConfig.privateKey, - fee + 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( - { - cid: result.id, - lrn: `${lrn}@${applicationRecord.repository_ref}` - }, - this.registryConfig.privateKey, - fee + await registryTransactionWithRetry(() => + this.registry.setName( + { + cid: result.id, + lrn: `${lrn}@${applicationRecord.repository_ref}` + }, + this.registryConfig.privateKey, + fee + ) ); return { @@ -183,19 +191,21 @@ export class Registry { const auctionConfig = config.auction; const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees); - const auctionResult = await this.registry.createProviderAuction( - { - commitFee: auctionConfig.commitFee, - commitsDuration: auctionConfig.commitsDuration, - revealFee: auctionConfig.revealFee, - revealsDuration: auctionConfig.revealsDuration, - denom: auctionConfig.denom, - maxPrice: auctionParams.maxPrice, - numProviders: auctionParams.numProviders, - }, - this.registryConfig.privateKey, - fee - ) + const auctionResult = await registryTransactionWithRetry(() => + this.registry.createProviderAuction( + { + commitFee: auctionConfig.commitFee, + commitsDuration: auctionConfig.commitsDuration, + revealFee: auctionConfig.revealFee, + revealsDuration: auctionConfig.revealsDuration, + denom: auctionConfig.denom, + maxPrice: auctionParams.maxPrice, + numProviders: auctionParams.numProviders, + }, + this.registryConfig.privateKey, + fee + ) + ); if (!auctionResult.auction) { throw new Error('Error creating auction'); @@ -208,14 +218,16 @@ export class Registry { type: APP_DEPLOYMENT_AUCTION_RECORD_TYPE, }; - const result = await this.registry.setRecord( - { - privateKey: this.registryConfig.privateKey, - record: applicationDeploymentAuction, - bondId: this.registryConfig.bondId - }, - this.registryConfig.privateKey, - fee + const result = await registryTransactionWithRetry(() => + this.registry.setRecord( + { + privateKey: this.registryConfig.privateKey, + record: applicationDeploymentAuction, + bondId: this.registryConfig.bondId + }, + this.registryConfig.privateKey, + fee + ) ); log(`Application deployment auction created: ${auctionResult.auction.id}`); @@ -274,14 +286,16 @@ export class Registry { const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees); - const result = await this.registry.setRecord( - { - privateKey: this.registryConfig.privateKey, - record: applicationDeploymentRequest, - bondId: this.registryConfig.bondId - }, - this.registryConfig.privateKey, - fee + const result = await registryTransactionWithRetry(() => + this.registry.setRecord( + { + privateKey: this.registryConfig.privateKey, + record: applicationDeploymentRequest, + bondId: this.registryConfig.bondId + }, + this.registryConfig.privateKey, + fee + ) ); log(`Application deployment request record published: ${result.id}`); @@ -322,12 +336,14 @@ export class Registry { auctionId: string ): Promise { const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees); - const auction = await this.registry.releaseFunds( - { - auctionId - }, - this.registryConfig.privateKey, - fee + const auction = await registryTransactionWithRetry(() => + this.registry.releaseFunds( + { + auctionId + }, + this.registryConfig.privateKey, + fee + ) ); return auction; @@ -424,14 +440,16 @@ export class Registry { const fee = parseGasAndFees(this.registryConfig.fee.gas, this.registryConfig.fee.fees); - const result = await this.registry.setRecord( - { - privateKey: this.registryConfig.privateKey, - record: applicationDeploymentRemovalRequest, - bondId: this.registryConfig.bondId - }, - this.registryConfig.privateKey, - fee + const result = await registryTransactionWithRetry(() => + this.registry.setRecord( + { + privateKey: this.registryConfig.privateKey, + record: applicationDeploymentRemovalRequest, + bondId: this.registryConfig.bondId + }, + this.registryConfig.privateKey, + fee + ) ); log(`Application deployment removal request record published: ${result.id}`); diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index 86e5147c..2b28259a 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -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 +): Promise => { + 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}`); + } + } +} diff --git a/packages/frontend/src/components/projects/Dialog/DeleteDeploymentDialog.tsx b/packages/frontend/src/components/projects/Dialog/DeleteDeploymentDialog.tsx index cc10f77a..a4bf4f92 100644 --- a/packages/frontend/src/components/projects/Dialog/DeleteDeploymentDialog.tsx +++ b/packages/frontend/src/components/projects/Dialog/DeleteDeploymentDialog.tsx @@ -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', diff --git a/packages/frontend/src/components/projects/create/Configure.tsx b/packages/frontend/src/components/projects/create/Configure.tsx index 9e6a45a7..91e7e60b 100644 --- a/packages/frontend/src/components/projects/create/Configure.tsx +++ b/packages/frontend/src/components/projects/create/Configure.tsx @@ -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'; @@ -156,32 +160,32 @@ const Configure = () => { if (templateId) { createFormData.option === 'Auction' ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, - ) + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) : navigate( - `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}`, - ); + `/${orgSlug}/projects/create/template/deploy?projectId=${projectId}&templateId=${templateId}`, + ); } else { createFormData.option === 'Auction' ? navigate( - `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, - ) + `/${orgSlug}/projects/create/success/${projectId}?isAuction=true`, + ) : navigate( - `/${orgSlug}/projects/create/deploy?projectId=${projectId}`, - ); + `/${orgSlug}/projects/create/deploy?projectId=${projectId}`, + ); } }, [client, createProject, dismiss, toast], ); 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 (
@@ -209,7 +213,7 @@ const Configure = () => { - {fieldState.error && {fieldState.error.message}} + {fieldState.error && ( + + {fieldState.error.message} + + )} )} /> diff --git a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx index fa6142e9..17bd996f 100644 --- a/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx +++ b/packages/frontend/src/components/projects/project/deployments/DeploymentDetailsCard.tsx @@ -185,8 +185,8 @@ const DeploymentDetailsCard = ({ type="orange" initials={getInitials(deployment.createdBy.name ?? '')} className="lg:size-5 2xl:size-6" - // TODO: Add avatarUrl - // imageSrc={deployment.createdBy.avatarUrl} + // TODO: Add avatarUrl + // imageSrc={deployment.createdBy.avatarUrl} >
{ -
Auction Status diff --git a/packages/frontend/src/stories/MockStoriesData.ts b/packages/frontend/src/stories/MockStoriesData.ts index 3d0e46f5..49e67885 100644 --- a/packages/frontend/src/stories/MockStoriesData.ts +++ b/packages/frontend/src/stories/MockStoriesData.ts @@ -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',