Handle account sequence mismatch error (#13)

Part of [Service provider auctions for web deployments](https://www.notion.so/Service-provider-auctions-for-web-deployments-104a6b22d47280dbad51d28aa3a91d75)

- Handle failed txs due to `account sequence mismatch` error by creating a wrapper for all tx methods and retry the tx if `account sequence mismatch` error occurs

Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
Reviewed-on: cerc-io/snowballtools-base#13
This commit is contained in:
2024-10-24 11:38:17 +00:00
co-authored by IshaVenikar
parent 3d9aedeb7e
commit 3fa60f3cdf
8 changed files with 182 additions and 113 deletions
@@ -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',
@@ -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 (
<div className="space-y-7 px-4 py-6">
@@ -209,11 +213,18 @@ const Configure = () => {
<Select
value={value}
onChange={(event) => onChange(event.target.value)}
size='small'
size="small"
displayEmpty
sx={{
fontFamily: 'inherit',
'& .MuiOutlinedInput-notchedOutline': {
borderColor: '#e0e0e0',
borderRadius: '8px',
},
}}
>
<MenuItem value="LRN">Deployer LRN</MenuItem>
<MenuItem value="Auction">Create Auction</MenuItem>
<MenuItem value="LRN">Deployer LRN</MenuItem>
</Select>
)}
/>
@@ -240,15 +251,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>
)}
/>
@@ -12,6 +12,7 @@ import {
DialogTitle,
DialogContent,
DialogActions,
Tooltip,
} from '@mui/material';
import { Avatar } from 'components/shared/Avatar';
@@ -104,16 +105,18 @@ const DeploymentDetailsCard = ({
const renderDeploymentStatus = useCallback(
(className?: string) => {
return (
<div className={className}>
<Tag
leftIcon={getIconByDeploymentStatus(deployment.status)}
size="xs"
type={STATUS_COLORS[deployment.status] ?? 'neutral'}
onClick={fetchDeploymentLogs}
>
{deployment.status}
</Tag>
</div>
<Tooltip title="Click to view build logs">
<div className={className} style={{ cursor: 'pointer' }}>
<Tag
leftIcon={getIconByDeploymentStatus(deployment.status)}
size="xs"
type={STATUS_COLORS[deployment.status] ?? 'neutral'}
onClick={fetchDeploymentLogs}
>
{deployment.status}
</Tag>
</div>
</Tooltip>
);
},
[deployment.status, deployment.commitHash],
@@ -185,8 +188,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}
></Avatar>
</div>
<OverflownText
@@ -56,7 +56,7 @@ export const DeploymentMenu = ({
const updateDeployment = async () => {
const isUpdated = await client.updateDeploymentToProd(deployment.id);
if (isUpdated) {
if (isUpdated.updateDeploymentToProd) {
await onUpdate();
toast({
id: 'deployment_changed_to_production',
@@ -77,7 +77,7 @@ export const DeploymentMenu = ({
const redeployToProd = async () => {
const isRedeployed = await client.redeployToProd(deployment.id);
setConfirmButtonLoadingLoading(false);
if (isRedeployed) {
if (isRedeployed.redeployToProd) {
await onUpdate();
toast({
id: 'redeployed_to_production',
@@ -100,7 +100,7 @@ export const DeploymentMenu = ({
project.id,
deployment.id,
);
if (isRollbacked) {
if (isRollbacked.rollbackDeployment) {
await onUpdate();
toast({
id: 'deployment_rolled_back',
@@ -124,7 +124,7 @@ export const DeploymentMenu = ({
setIsConfirmDeleteLoading(false);
setDeleteDeploymentDialog((preVal) => !preVal);
if (isDeleted) {
if (isDeleted.deleteDeployment) {
await onUpdate();
toast({
id: 'deployment_removal_requested',
@@ -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
@@ -131,7 +130,10 @@ export const AuctionCard = ({ project }: { project: Project }) => {
Deployer Funds Status
</span>
<div className="ml-2">
<Tag size="xs" type={fundsStatus ? 'positive' : 'emphasized'}>
<Tag
size="xs"
type={fundsStatus ? 'positive' : 'emphasized'}
>
{fundsStatus ? 'RELEASED' : 'LOCKED'}
</Tag>
</div>
@@ -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',