Skip to content

Commit

Permalink
feat(console): managed wallets popup confirmation (#342)
Browse files Browse the repository at this point in the history
  • Loading branch information
baktun14 authored Aug 30, 2024
1 parent 155113c commit c7d16d6
Show file tree
Hide file tree
Showing 15 changed files with 170 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ export class UserWalletRepository extends BaseRepository<ApiPgTables["UserWallet
protected toInput({ deploymentAllowance, feeAllowance, ...input }: UserWalletInput): DbUserWalletInput {
const dbInput: DbUserWalletInput = input;

if (deploymentAllowance) {
if (deploymentAllowance !== undefined) {
dbInput.deploymentAllowance = deploymentAllowance.toString();
}

if (feeAllowance) {
if (feeAllowance !== undefined) {
dbInput.feeAllowance = feeAllowance.toString();
}

Expand Down
3 changes: 3 additions & 0 deletions apps/api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import "./dotenv";

async function bootstrap() {
/* eslint-disable @typescript-eslint/no-var-requires */
console.log("Bootstrapping", process.env);
if (process.env.BILLING_ENABLED === "true") {
console.log("Billing enabled");
const pg = require("./core");
await pg.migratePG();
console.log("PG Migrations complete");
}

const entry = require("./app");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { event } from "nextjs-google-analytics";
import { CustomDropdownLinkItem } from "@src/components/shared/CustomDropdownLinkItem";
import { useLocalNotes } from "@src/context/LocalNoteProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { usePreviousRoute } from "@src/hooks/usePreviousRoute";
import { DeploymentDto } from "@src/types/deployment";
import { AnalyticsEvents } from "@src/utils/analytics";
Expand All @@ -28,12 +29,13 @@ type Props = {
export const DeploymentDetailTopBar: React.FunctionComponent<Props> = ({ address, loadDeploymentDetail, removeLeases, setActiveTab, deployment }) => {
const { changeDeploymentName, getDeploymentData, getDeploymentName } = useLocalNotes();
const router = useRouter();
const { signAndBroadcastTx } = useWallet();
const { signAndBroadcastTx, isManaged } = useWallet();
const [isDepositingDeployment, setIsDepositingDeployment] = useState(false);
const storageDeploymentData = getDeploymentData(deployment?.dseq);
const deploymentName = getDeploymentName(deployment?.dseq);
const previousRoute = usePreviousRoute();
const wallet = useWallet();
const { closeDeploymentConfirm } = useManagedDeploymentConfirm();

function handleBackClick() {
if (previousRoute) {
Expand All @@ -44,6 +46,12 @@ export const DeploymentDetailTopBar: React.FunctionComponent<Props> = ({ address
}

const onCloseDeployment = async () => {
const isConfirmed = await closeDeploymentConfirm([deployment.dseq]);

if (!isConfirmed) {
return;
}

const message = TransactionMessageData.getCloseDeploymentMsg(address, deployment.dseq);
const response = await signAndBroadcastTx([message]);
if (response) {
Expand Down
8 changes: 8 additions & 0 deletions apps/deploy-web/src/components/deployments/DeploymentList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { LinkTo } from "@src/components/shared/LinkTo";
import { useLocalNotes } from "@src/context/LocalNoteProvider";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { useDeploymentList } from "@src/queries/useDeploymentQuery";
import { useProviderList } from "@src/queries/useProvidersQuery";
import sdlStore from "@src/store/sdlStore";
Expand Down Expand Up @@ -54,6 +55,7 @@ export const DeploymentList: React.FunctionComponent = () => {
const currentPageDeployments = orderedDeployments.slice(start, end);
const pageCount = Math.ceil(orderedDeployments.length / pageSize);
const [, setDeploySdl] = useAtom(sdlStore.deploySdl);
const { closeDeploymentConfirm } = useManagedDeploymentConfirm();

useEffect(() => {
if (isWalletLoaded && isSettingsInit) {
Expand Down Expand Up @@ -109,6 +111,12 @@ export const DeploymentList: React.FunctionComponent = () => {

const onCloseSelectedDeployments = async () => {
try {
const isConfirmed = await closeDeploymentConfirm(selectedDeploymentDseqs);

if (!isConfirmed) {
return;
}

const messages = selectedDeploymentDseqs.map(dseq => TransactionMessageData.getCloseDeploymentMsg(address, `${dseq}`));
const response = await signAndBroadcastTx(messages);
if (response) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useRouter } from "next/navigation";
import { event } from "nextjs-google-analytics";

import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { getShortText } from "@src/hooks/useShortText";
import { useDenomData } from "@src/hooks/useWalletBalance";
import { useAllLeases } from "@src/queries/useLeaseQuery";
Expand Down Expand Up @@ -110,6 +111,7 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
const avgCost = udenomToDenom(getAvgCostPerMonth(deploymentCost || 0));
const storageDeploymentData = getDeploymentData(deployment?.dseq);
const denomData = useDenomData(deployment.escrowAccount.balance.denom);
const { closeDeploymentConfirm } = useManagedDeploymentConfirm();

function viewDeployment() {
router.push(UrlService.deploymentDetails(deployment.dseq));
Expand Down Expand Up @@ -143,6 +145,12 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
const onCloseDeployment = async () => {
handleMenuClose();

const isConfirmed = await closeDeploymentConfirm([deployment.dseq]);

if (!isConfirmed) {
return;
}

const message = TransactionMessageData.getCloseDeploymentMsg(address, deployment.dseq);
const response = await signAndBroadcastTx([message]);
if (response) {
Expand Down
6 changes: 1 addition & 5 deletions apps/deploy-web/src/components/deployments/LeaseRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,7 @@ export const LeaseRow = React.forwardRef<AcceptRefType, Props>(({ lease, setActi
}
}
});
const {
data: providerStatus,
isLoading: isLoadingProviderStatus,
refetch: getProviderStatus
} = useProviderStatus(provider?.hostUri || "", {
const { isLoading: isLoadingProviderStatus, refetch: getProviderStatus } = useProviderStatus(provider?.hostUri || "", {
enabled: false,
retry: false
});
Expand Down
8 changes: 8 additions & 0 deletions apps/deploy-web/src/components/new-deployment/CreateLease.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useSnackbar } from "notistack";

import { LocalCert } from "@src/context/CertificateProvider/CertificateProviderContext";
import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { useBidList } from "@src/queries/useBidQuery";
import { useDeploymentDetail } from "@src/queries/useDeploymentQuery";
import { useProviderList } from "@src/queries/useProvidersQuery";
Expand Down Expand Up @@ -88,6 +89,7 @@ export const CreateLease: React.FunctionComponent<Props> = ({ dseq }) => {
const allClosed = (bids?.length || 0) > 0 && bids?.every(bid => bid.state === "closed");
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
const wallet = useWallet();
const { closeDeploymentConfirm } = useManagedDeploymentConfirm();

useEffect(() => {
getDeploymentDetail();
Expand Down Expand Up @@ -202,6 +204,12 @@ export const CreateLease: React.FunctionComponent<Props> = ({ dseq }) => {
}

async function handleCloseDeployment() {
const isConfirmed = await closeDeploymentConfirm([dseq]);

if (!isConfirmed) {
return;
}

const message = TransactionMessageData.getCloseDeploymentMsg(address, dseq);
const response = await signAndBroadcastTx([message]);

Expand Down
16 changes: 15 additions & 1 deletion apps/deploy-web/src/components/new-deployment/ManifestEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useCertificate } from "@src/context/CertificateProvider";
import { useChainParam } from "@src/context/ChainParamProvider";
import { useSdlBuilder } from "@src/context/SdlBuilderProvider/SdlBuilderProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { useManagedWalletDenom } from "@src/hooks/useManagedWalletDenom";
import { useWhen } from "@src/hooks/useWhen";
import { useDepositParams } from "@src/queries/useSettings";
Expand All @@ -26,6 +27,7 @@ import { defaultInitialDeposit, RouteStepKeys } from "@src/utils/constants";
import { deploymentData } from "@src/utils/deploymentData";
import { saveDeploymentManifestAndName } from "@src/utils/deploymentLocalDataUtils";
import { validateDeploymentData } from "@src/utils/deploymentUtils";
import { importSimpleSdl } from "@src/utils/sdl/sdlImport";
import { cn } from "@src/utils/styleUtils";
import { Timer } from "@src/utils/timer";
import { TransactionMessageData } from "@src/utils/TransactionMessageData";
Expand Down Expand Up @@ -72,6 +74,7 @@ export const ManifestEdit: React.FunctionComponent<Props> = ({ editedManifest, s
const fileUploadRef = useRef<HTMLInputElement>(null);
const wallet = useWallet();
const managedDenom = useManagedWalletDenom();
const { createDeploymentConfirm } = useManagedDeploymentConfirm();

useWhen(wallet.isManaged && sdlDenom === "uakt", () => {
setSdlDenom(managedDenom);
Expand Down Expand Up @@ -182,7 +185,18 @@ export const ManifestEdit: React.FunctionComponent<Props> = ({ editedManifest, s
}

if (isManaged) {
await handleCreateClick(defaultDeposit, envConfig.NEXT_PUBLIC_MASTER_WALLET_ADDRESS);
const services = importSimpleSdl(editedManifest as string);

if (!services) {
setParsingError("Error while parsing SDL file");
return;
}

const isConfirmed = await createDeploymentConfirm(services);

if (isConfirmed) {
await handleCreateClick(defaultDeposit, envConfig.NEXT_PUBLIC_MASTER_WALLET_ADDRESS);
}
} else {
setIsCheckingPrerequisites(true);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import { Button, buttonVariants } from "@akashnetwork/ui/components";
import { ArrowRight, Cpu, Linux,Page, Rocket, Wrench } from "iconoir-react";
import { ArrowRight, Cpu, Linux, Rocket, Wrench } from "iconoir-react";
import { NavArrowLeft } from "iconoir-react";
import { useAtom } from "jotai";
import Link from "next/link";
Expand Down
48 changes: 41 additions & 7 deletions apps/deploy-web/src/components/sdl/RentGpusForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ import { useAtom } from "jotai";
import { useRouter, useSearchParams } from "next/navigation";
import { event } from "nextjs-google-analytics";

import { envConfig } from "@src/config/env.config";
import { useCertificate } from "@src/context/CertificateProvider";
import { useChainParam } from "@src/context/ChainParamProvider";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useManagedDeploymentConfirm } from "@src/hooks/useManagedDeploymentConfirm";
import { useManagedWalletDenom } from "@src/hooks/useManagedWalletDenom";
import { useWhen } from "@src/hooks/useWhen";
import { useGpuModels } from "@src/queries/useGpuQuery";
import { useDepositParams } from "@src/queries/useSettings";
import sdlStore from "@src/store/sdlStore";
import { ApiTemplate, ProfileGpuModelType, RentGpusFormValuesSchema, RentGpusFormValuesType, ServiceType } from "@src/types";
import { DepositParams } from "@src/types/deployment";
import { ProviderAttributeSchemaDetailValue } from "@src/types/providerAttributes";
import { AnalyticsEvents } from "@src/utils/analytics";
import { defaultInitialDeposit, RouteStepKeys } from "@src/utils/constants";
Expand Down Expand Up @@ -65,11 +71,20 @@ export const RentGpusForm: React.FunctionComponent = () => {
const searchParams = useSearchParams();
const currentService: ServiceType = (_services && _services[0]) || ({} as any);
const { settings } = useSettings();
const { address, signAndBroadcastTx } = useWallet();
const { address, signAndBroadcastTx, isManaged } = useWallet();
const { loadValidCertificates, localCert, isLocalCertMatching, loadLocalCert, setSelectedCertificate } = useCertificate();
const [sdlDenom, setSdlDenom] = useState("uakt");
const { minDeposit } = useChainParam();
const router = useRouter();
const { createDeploymentConfirm } = useManagedDeploymentConfirm();
const managedDenom = useManagedWalletDenom();
const { data: depositParams } = useDepositParams();
const defaultDeposit = depositParams || defaultInitialDeposit;

useWhen(isManaged && sdlDenom === "uakt", () => {
setSdlDenom(managedDenom);
setValue("services.0.placement.pricing.denom", managedDenom);
});

useEffect(() => {
if (rentGpuSdl && rentGpuSdl.services) {
Expand Down Expand Up @@ -113,7 +128,12 @@ export const RentGpusForm: React.FunctionComponent = () => {
}
}, [searchParams, gpuModels, isQueryInit]);

async function createAndValidateDeploymentData(yamlStr: string, dseq = null, deposit = defaultInitialDeposit, depositorAddress: string | null = null) {
async function createAndValidateDeploymentData(
yamlStr: string,
dseq: string | null = null,
deposit = defaultDeposit,
depositorAddress: string | null = null
) {
try {
if (!yamlStr) return null;

Expand Down Expand Up @@ -168,6 +188,7 @@ export const RentGpusForm: React.FunctionComponent = () => {

setValue("services", result as ServiceType[]);
setValue("services.0.profile.gpuModels", _gpuModels);
setValue("services.0.placement.pricing.denom", managedDenom);
trigger();
};

Expand All @@ -183,10 +204,21 @@ export const RentGpusForm: React.FunctionComponent = () => {

const onSubmit = async (data: RentGpusFormValuesType) => {
setRentGpuSdl(data);
setIsCheckingPrerequisites(true);

if (isManaged) {
const isConfirmed = await createDeploymentConfirm(rentGpuSdl?.services as ServiceType[]);

if (!isConfirmed) {
return;
}

await handleCreateClick(defaultDeposit, envConfig.NEXT_PUBLIC_MASTER_WALLET_ADDRESS);
} else {
setIsCheckingPrerequisites(true);
}
};

async function handleCreateClick(deposit: number, depositorAddress: string) {
async function handleCreateClick(deposit: number | DepositParams[], depositorAddress: string) {
setError(null);

try {
Expand Down Expand Up @@ -309,9 +341,11 @@ export const RentGpusForm: React.FunctionComponent = () => {
<div>
<RegionSelect control={control} />
</div>
<div>
<TokenFormControl control={control} name="services.0.placement.pricing.denom" />
</div>
{!isManaged && (
<div>
<TokenFormControl control={control} name="services.0.placement.pricing.denom" />
</div>
)}
</div>
</FormPaper>

Expand Down
67 changes: 67 additions & 0 deletions apps/deploy-web/src/hooks/useManagedDeploymentConfirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { usePopup } from "@akashnetwork/ui/context";

import { LeaseSpecDetail } from "@src/components/shared/LeaseSpecDetail";
import { useWallet } from "@src/context/WalletProvider";
import { ServiceType } from "@src/types";

export const useManagedDeploymentConfirm = () => {
const { isManaged } = useWallet();
const { confirm } = usePopup();

const closeDeploymentConfirm = async (dseq: string[]) => {
if (isManaged) {
const isConfirmed = await confirm({
title: `Are you sure you want to close ${dseq.length > 1 ? "these deployments" : "this deployment"}?`,
message: (
<div className="space-y-2">
<p className="text-sm">
DSEQ <span className="text-xs text-muted-foreground">({dseq.join(",")})</span>
</p>
<p className="text-sm text-muted-foreground">Closing a deployment will stop all services and release any unused escrowed funds.</p>
</div>
)
});

if (!isConfirmed) {
return false;
}
}

return true;
};

const createDeploymentConfirm = async (services: ServiceType[]) => {
if (isManaged) {
const isConfirmed = await confirm({
title: "Confirm deployment creation?",
message: (
<div className="space-y-2">
{services.map(service => {
return (
<div key={service.image} className="rounded border p-4">
<div className="mb-2 text-sm">
<span className="font-bold">{service.title}</span>:{service.image}
</div>
<div className="flex items-center space-x-4 whitespace-nowrap">
<LeaseSpecDetail type="cpu" className="flex-shrink-0" value={service.profile?.cpu as number} />
{service.profile?.hasGpu && <LeaseSpecDetail type="gpu" className="flex-shrink-0" value={service.profile?.gpu as number} />}
<LeaseSpecDetail type="ram" className="flex-shrink-0" value={`${service.profile?.ram} ${service.profile?.ramUnit}`} />
<LeaseSpecDetail type="storage" className="flex-shrink-0" value={`${service.profile?.storage} ${service.profile?.storageUnit}`} />
</div>
</div>
);
})}
</div>
)
});

if (!isConfirmed) {
return false;
}
}

return true;
};

return { closeDeploymentConfirm, createDeploymentConfirm };
};
Loading

0 comments on commit c7d16d6

Please sign in to comment.