Skip to content

Commit

Permalink
feat(deployment): implement deployment deposit top up via managed wallet
Browse files Browse the repository at this point in the history
refs #247
  • Loading branch information
ygrishajev committed Sep 13, 2024
1 parent a6752e4 commit 2f0e611
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export const SignTxRequestInputSchema = z.object({
"/akash.cert.v1beta3.MsgCreateCertificate",
"/akash.market.v1beta4.MsgCreateLease",
"/akash.deployment.v1beta3.MsgUpdateDeployment",
"/akash.deployment.v1beta3.MsgCloseDeployment"
"/akash.deployment.v1beta3.MsgCloseDeployment",
"/akash.deployment.v1beta3.MsgDepositDeployment"
]),
value: z.string()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc

const onBalanceClick = () => {
clearErrors();
setValue("amount", denomData?.inputMax || 0);
setValue("amount", denomData?.max || 0);
};

return (
Expand Down Expand Up @@ -143,7 +143,7 @@ export const AllowanceModal: React.FunctionComponent<Props> = ({ editingAllowanc
autoFocus
min={0}
step={0.000001}
max={denomData?.inputMax}
max={denomData?.max}
startIcon={<span className="pl-2 text-xs">{denomData?.label}</span>}
/>
);
Expand Down
4 changes: 2 additions & 2 deletions apps/deploy-web/src/components/authorizations/GrantModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const GrantModal: React.FunctionComponent<Props> = ({ editingGrant, addre

const onBalanceClick = () => {
clearErrors();
setValue("amount", denomData?.inputMax || 0);
setValue("amount", denomData?.max || 0);
};

return (
Expand Down Expand Up @@ -187,7 +187,7 @@ export const GrantModal: React.FunctionComponent<Props> = ({ editingGrant, addre
autoFocus
min={0}
step={0.000001}
max={denomData?.inputMax}
max={denomData?.max}
startIcon={<span className="pl-2 text-xs">{denomData?.label}</span>}
className="ml-4 flex-grow"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ import { event } from "nextjs-google-analytics";
import { useSnackbar } from "notistack";
import { z } from "zod";

import { browserEnvConfig } from "@src/config/browser-env.config";
import { UAKT_DENOM } from "@src/config/denom.config";
import { usePricing } from "@src/context/PricingProvider";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useUsdcDenom } from "@src/hooks/useDenom";
import { useDenomData, useWalletBalance } from "@src/hooks/useWalletBalance";
import { useGranteeGrants } from "@src/queries/useGrantsQuery";
import { AnalyticsEvents } from "@src/utils/analytics";
import { denomToUdenom, udenomToDenom } from "@src/utils/mathHelpers";
import { coinToUDenom, uaktToAKT } from "@src/utils/priceUtils";
import { coinToUDenom } from "@src/utils/priceUtils";
import { LinkTo } from "../shared/LinkTo";
import { GranteeDepositMenuItem } from "./GranteeDepositMenuItem";

type Props = {
export type DeploymentDepositModalProps = {
infoText?: string | ReactNode;
disableMin?: boolean;
denom: string;
Expand Down Expand Up @@ -67,15 +68,22 @@ const formSchema = z
{ message: "Depositor address is required.", path: ["depositorAddress"] }
);

export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleCancel, onDeploymentDeposit, disableMin, denom, infoText = null }) => {
export const DeploymentDepositModal: React.FunctionComponent<DeploymentDepositModalProps> = ({
handleCancel,
onDeploymentDeposit,
disableMin,
denom,
infoText = null
}) => {
const formRef = useRef<HTMLFormElement>(null);
const { settings } = useSettings();
const { enqueueSnackbar } = useSnackbar();
const [error, setError] = useState("");
const [isCheckingDepositor, setIsCheckingDepositor] = useState(false);
const { address } = useWallet();
const { address, isManaged, isCustodial } = useWallet();
const { balance: walletBalance } = useWalletBalance();
const { data: granteeGrants } = useGranteeGrants(address);
const pricing = usePricing();
const depositData = useDenomData(denom);
const form = useForm<z.infer<typeof formSchema>>({
defaultValues: {
Expand All @@ -87,7 +95,6 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC
});
const { handleSubmit, control, watch, setValue, clearErrors, unregister } = form;
const { amount, useDepositor, depositorAddress } = watch();
const usdcIbcDenom = useUsdcDenom();
const validGrants = granteeGrants?.filter(x => compareAsc(new Date(), new Date(x.expiration)) !== 1 && x.authorization.spend_limit.denom === denom) || [];

useEffect(() => {
Expand Down Expand Up @@ -156,7 +163,7 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC

const onBalanceClick = () => {
clearErrors();
setValue("amount", depositData?.inputMax || 0);
setValue("amount", depositData?.max || 0);
};

const onDepositClick = event => {
Expand All @@ -167,9 +174,8 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC
const onSubmit = async ({ amount, depositorAddress }: z.infer<typeof formSchema>) => {
setError("");
clearErrors();
const deposit = denomToUdenom(amount);
const uaktBalance = walletBalance?.balanceUAKT || 0;
const usdcBalance = walletBalance?.balanceUUSDC || 0;
const amountInDenom = (isManaged && denom === UAKT_DENOM ? pricing.usdToAkt(amount) : amount) || 0;
const deposit = denomToUdenom(amountInDenom);

if (!disableMin && amount < (depositData?.min || 0)) {
setError(`Deposit amount must be greater or equal than ${depositData?.min}.`);
Expand All @@ -186,15 +192,12 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC
category: "deployments",
label: "Use depositor to deposit in deployment"
});
} else if (denom === UAKT_DENOM && deposit > uaktBalance) {
setError(`You can't deposit more than you currently have in your balance. Current balance is: ${uaktToAKT(uaktBalance)} AKT.`);
return;
} else if (denom === usdcIbcDenom && deposit > usdcBalance) {
setError(`You can't deposit more than you currently have in your balance. Current balance is: ${udenomToDenom(usdcBalance)} USDC.`);
} else if (depositData && amountInDenom > depositData?.balance) {
setError(`You can't deposit more than you currently have in your balance. Current balance is: ${depositData?.balance} ${depositData?.label}.`);
return;
}

onDeploymentDeposit(deposit, depositorAddress as string);
onDeploymentDeposit(deposit, isManaged ? browserEnvConfig.NEXT_PUBLIC_MASTER_WALLET_ADDRESS : (depositorAddress as string));
};

return (
Expand Down Expand Up @@ -249,23 +252,25 @@ export const DeploymentDepositModal: React.FunctionComponent<Props> = ({ handleC
autoFocus
min={!disableMin ? depositData?.min : 0}
step={0.000001}
max={depositData?.inputMax}
max={depositData?.max}
startIcon={<div className="pl-2 text-xs">{depositData?.label}</div>}
/>
);
}}
/>
</div>

<div className="my-4 flex items-center">
<Controller
control={control}
name="useDepositor"
render={({ field }) => {
return <CheckboxWithLabel label="Use another address to fund" checked={field.value} onCheckedChange={field.onChange} />;
}}
/>
</div>
{isCustodial && (
<div className="my-4 flex items-center">
<Controller
control={control}
name="useDepositor"
render={({ field }) => {
return <CheckboxWithLabel label="Use another address to fund" checked={field.value} onCheckedChange={field.onChange} />;
}}
/>
</div>
)}

{useDepositor && (
<FormField
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ type Props = {
export const DeploymentDetailTopBar: React.FunctionComponent<Props> = ({ address, loadDeploymentDetail, removeLeases, setActiveTab, deployment }) => {
const { changeDeploymentName, getDeploymentData, getDeploymentName } = useLocalNotes();
const router = useRouter();
const { signAndBroadcastTx, isManaged } = useWallet();
const { signAndBroadcastTx } = 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() {
Expand Down Expand Up @@ -126,11 +125,9 @@ export const DeploymentDetailTopBar: React.FunctionComponent<Props> = ({ address
</DropdownMenuContent>
</DropdownMenu>

{!wallet.isManaged && (
<Button variant="default" className="ml-2 whitespace-nowrap" onClick={() => setIsDepositingDeployment(true)} size="sm">
Add funds
</Button>
)}
<Button variant="default" className="ml-2 whitespace-nowrap" onClick={() => setIsDepositingDeployment(true)} size="sm">
Add funds
</Button>
</div>
)}

Expand Down
16 changes: 6 additions & 10 deletions apps/deploy-web/src/components/deployments/DeploymentListRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { CustomDropdownLinkItem } from "../shared/CustomDropdownLinkItem";
import { PricePerMonth } from "../shared/PricePerMonth";
import { PriceValue } from "../shared/PriceValue";
import { SpecDetailList } from "../shared/SpecDetailList";
import { DeploymentDepositModal } from "./DeploymentDepositModal";
import { DeploymentDepositModal, DeploymentDepositModalProps } from "./DeploymentDepositModal";
import { LeaseChip } from "./LeaseChip";

type Props = {
Expand Down Expand Up @@ -127,7 +127,7 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
setOpen(false);
};

const onDeploymentDeposit = async (deposit, depositorAddress) => {
const onDeploymentDeposit: DeploymentDepositModalProps["onDeploymentDeposit"] = async (deposit, depositorAddress) => {
setIsDepositingDeployment(false);

const message = TransactionMessageData.getDepositDeploymentMsg(address, deployment.dseq, deposit, deployment.escrowAccount.balance.denom, depositorAddress);
Expand Down Expand Up @@ -213,13 +213,9 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
title={
<>
Your deployment will close soon,{" "}
{isManagedWallet ? (
"Add funds"
) : (
<a href="#" onClick={showDepositModal}>
Add Funds
</a>
)}{" "}
<a href="#" onClick={showDepositModal}>
Add Funds
</a>{" "}
to keep it running.
</>
}
Expand Down Expand Up @@ -336,7 +332,7 @@ export const DeploymentListRow: React.FunctionComponent<Props> = ({ deployment,
>
<ClickAwayListener onClickAway={() => setOpen(false)}>
<div>
{isActive && isManagedWallet && (
{isActive && (
<CustomDropdownLinkItem onClick={showDepositModal} icon={<Plus fontSize="small" />}>
Add funds
</CustomDropdownLinkItem>
Expand Down
73 changes: 41 additions & 32 deletions apps/deploy-web/src/components/deployments/DeploymentSubHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LabelValue } from "@src/components/shared/LabelValue";
import { PricePerMonth } from "@src/components/shared/PricePerMonth";
import { PriceValue } from "@src/components/shared/PriceValue";
import { StatusPill } from "@src/components/shared/StatusPill";
import { useWallet } from "@src/context/WalletProvider";
import { useDenomData } from "@src/hooks/useWalletBalance";
import { DeploymentDto, LeaseDto } from "@src/types/deployment";
import { udenomToDenom } from "@src/utils/mathHelpers";
Expand All @@ -28,6 +29,7 @@ export const DeploymentSubHeader: React.FunctionComponent<Props> = ({ deployment
const isActive = deployment.state === "active";
const hasActiveLeases = hasLeases && leases.some(l => l.state === "active");
const denomData = useDenomData(deployment.escrowAccount.balance.denom);
const { isCustodial } = useWallet();

return (
<div className="grid grid-cols-2 gap-4 p-4">
Expand All @@ -41,19 +43,22 @@ export const DeploymentSubHeader: React.FunctionComponent<Props> = ({ deployment
denom={deployment.escrowAccount.balance.denom}
value={udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.escrow : deployment.escrowBalance, 6)}
/>
<CustomTooltip
title={
<>
<strong>
{udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.escrow : deployment.escrowBalance, 6)}&nbsp;{denomData?.label}
</strong>
<br />
The escrow account balance will be fully returned to your wallet balance when the deployment is closed.{" "}
</>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
{isCustodial && (
<CustomTooltip
title={
<>
<strong>
{udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.escrow : deployment.escrowBalance, 6)}&nbsp;
{denomData?.label}
</strong>
<br />
The escrow account balance will be fully returned to your wallet balance when the deployment is closed.{" "}
</>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
)}

{isActive && hasActiveLeases && !!realTimeLeft && realTimeLeft.escrow <= 0 && (
<CustomTooltip title="Your deployment is out of funds and can be closed by your provider at any time now. You can add funds to keep active.">
Expand All @@ -71,15 +76,17 @@ export const DeploymentSubHeader: React.FunctionComponent<Props> = ({ deployment
<div className="flex items-center space-x-2">
<PricePerMonth denom={deployment.escrowAccount.balance.denom} perBlockValue={udenomToDenom(deploymentCost, 10)} />

<CustomTooltip
title={
<span>
{avgCost} {denomData?.label} / month
</span>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
{isCustodial && (
<CustomTooltip
title={
<span>
{avgCost} {denomData?.label} / month
</span>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
)}
</div>
)
}
Expand All @@ -94,16 +101,18 @@ export const DeploymentSubHeader: React.FunctionComponent<Props> = ({ deployment
value={udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.amountSpent : parseFloat(deployment.transferred.amount), 6)}
/>

<CustomTooltip
title={
<span>
{udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.amountSpent : parseFloat(deployment.transferred.amount), 6)}{" "}
{denomData?.label}
</span>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
{isCustodial && (
<CustomTooltip
title={
<span>
{udenomToDenom(isActive && hasActiveLeases && realTimeLeft ? realTimeLeft?.amountSpent : parseFloat(deployment.transferred.amount), 6)}{" "}
{denomData?.label}
</span>
}
>
<InfoCircle className="text-xs text-muted-foreground" />
</CustomTooltip>
)}
</div>
}
/>
Expand Down
6 changes: 4 additions & 2 deletions apps/deploy-web/src/components/layout/TransactionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export type LoadingState =
| "creatingDeployment"
| "updatingDeployment"
| "creatingLease"
| "closingDeployment";
| "closingDeployment"
| "depositingDeployment";

type Props = {
state?: LoadingState;
Expand All @@ -24,7 +25,8 @@ const TITLES: Record<LoadingState, string> = {
creatingDeployment: "Creating Deployment",
updatingDeployment: "Updating Deployment",
creatingLease: "Creating Lease",
closingDeployment: "Closing Deployment"
closingDeployment: "Closing Deployment",
depositingDeployment: "Depositing Deployment"
};

const CRYPTO_STATES: LoadingState[] = ["waitingForApproval", "broadcasting"];
Expand Down
Loading

0 comments on commit 2f0e611

Please sign in to comment.