Skip to content

Claim single key submissions #478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2 changes: 1 addition & 1 deletion .github/workflows/signed-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
run: sed -i'' -e 's/VERSION_NUMBER/${{ env.VERSION }}/g' packages/core/src/utils/version.ts

- name: Build monorepo
run: npx nx run-many --target=build --all
run: npx nx run-many --target=build -p @sentry/cli @sentry/sentry-client-desktop
env:
CSC_LINK: ${{ secrets.MAC_CERTIFICATE_P12_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
Expand Down
136 changes: 136 additions & 0 deletions apps/sentry-client-desktop/src/components/ClaimBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { useState } from "react";
import BaseCallout from "@sentry/ui/src/rebrand/callout/BaseCallout";
import { PrimaryButton, WarningIcon } from "@sentry/ui";
import { AiOutlineClose } from "react-icons/ai";
import { useAtomValue } from "jotai";
import { chainStateAtom, useChainDataRefresh } from "@/hooks/useChainDataWithCallback";
import { useBalance } from "@/hooks/useBalance";
import { useOperator } from "@/features/operator";
import { useStorage } from "@/features/storage";
import { processUnclaimedChallenges } from "@sentry/core";
import toast from "react-hot-toast";

export function ClaimBanner() {

const {
unclaimedEsXaiFromSoloSubmission,
estimateGasForUnclaimed
} = useAtomValue(chainStateAtom);

const { refresh } = useChainDataRefresh();

const { publicKey, signer } = useOperator();
const { data: ethBalance } = useBalance(publicKey);
const { data } = useStorage();

const [isModalOpen, setIsModalOpen] = useState(false);
const [isProcessingUnclaimed, setIsProcessingUnclaimed] = useState(false);
const [claimError, setClaimError] = useState("");

const onOpenModal = () => {
setIsModalOpen(true)
}

const onCloseModal = () => {
setIsModalOpen(false)
}

const onClaim = async () => {
if (isProcessingUnclaimed) {
return;
}
if (!signer) {
return;
}

setIsProcessingUnclaimed(true);
setClaimError("");
try {
// Adding the "ts-ignore" to avoid ts error for different module exports of sentry core and the desktop client.
// @ts-ignore
await processUnclaimedChallenges(signer, console.log, data?.whitelistedWallets);
setIsProcessingUnclaimed(false);
setIsModalOpen(false);
toast.success("Successfully claimed previous submissions", {duration: 10000});
refresh();

} catch (error) {
console.error("Failed to process unclaimed", error);
setIsProcessingUnclaimed(false);
setClaimError("Failed to process");
}

}

return <>

{unclaimedEsXaiFromSoloSubmission > 0 && <BaseCallout extraClasses={{
calloutWrapper: "w-full bg-potBlack mb-4",
calloutFront: "!items-start flex-col bg-potBlack"
}}>
<div className="flex justify-between items-center w-full">
<div>
<p className="font-semibold text-cascadingWhite">
Rewards remaining from a previous version need to be manually claimed
</p>

<p className="text-elementalGrey">
{unclaimedEsXaiFromSoloSubmission} esXAI remaining

</p>
</div>
<PrimaryButton
onClick={onOpenModal}
btnText={"CLAIM"}
wrapperClassName={"w-max !keys-cta-button-clip-path"}
className={"!py-[6px] h-max !keys-cta-button-clip-path"}
colorStyle={"outline"}
/>
</div>
</BaseCallout>}

{isModalOpen && <div
className="fixed top-0 right-0 left-0 bottom-0 m-auto w-auto h-auto flex flex-col justify-start items-center z-[60]">
<div className="w-full h-full bg-black opacity-75" />
<div
className="absolute top-0 right-0 left-0 bottom-0 m-auto flex flex-col justify-start items-center w-[456px] h-max bg-dynamicBlack">
<div
className="p-4 w-full flex flex-col gap-4 text-cascadingWhite text-lg">
<div className="flex justify-between font-bold">
<p>Claim remaining rewards</p>
<span className="cursor-pointer" onClick={onCloseModal}>
<AiOutlineClose size={20} color="white"
className="hover:!text-hornetSting duration-300 ease-in" />
</span>
</div>
<p className="text-elementalGrey relative after:absolute after:w-[456px] after:h-[1px] after:bg-darkRoom after:bottom-[-16px] after:left-[-16px]">All
remaining rewards are from a previous version and will need to be manually claimed. ETH is
required to process this transaction.</p>
<div
className="flex w-full justify-between my-4 relative after:absolute after:w-[456px] after:h-[1px] after:bg-darkRoom after:bottom-[-16px] after:left-[-16px]">
<p className="text-elementalGrey">Remaining rewards</p>
<p>{unclaimedEsXaiFromSoloSubmission} esXAI</p>
</div>
<div
className="flex w-full justify-between my-4 relative after:absolute after:w-[456px] after:h-[1px] after:bg-darkRoom after:bottom-[-16px] after:left-[-16px]">
<p className="text-elementalGrey">Cost to claim</p>
<div className="flex flex-col items-end">
<p>~{estimateGasForUnclaimed} ETH</p>
<p className="text-base text-elementalGrey">Available: {Number(ethBalance?.ethString)} ETH</p>
</div>
</div>
{claimError && <div className="flex justify-center text-liquidLava gap-1">
<WarningIcon fill={"#F97316"} width={20} />
<p>Claim unsuccessful</p>
</div>}
<PrimaryButton
onClick={onClaim}
btnText={isProcessingUnclaimed ? "Loading..." : "Claim All"}
className="w-full text-xl"
isDisabled={isProcessingUnclaimed}
/>
</div>
</div>
</div>}
</>
}
8 changes: 6 additions & 2 deletions apps/sentry-client-desktop/src/features/home/SentryWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { CopyIcon, HelpIcon } from "@sentry/ui/dist/src/rebrand/icons/IconsCompo
import { SentryWalletTableBody } from "@/features/home/SentryWalletTableBody";
import { useBalance } from "@/hooks/useBalance";
import { InfoBanner } from "@/components/InfoBanner";
import { ClaimBanner } from "@/components/ClaimBanner";

// TODO -> replace with dynamic value later
export const recommendedFundingBalance = ethers.parseEther("0.005");
Expand Down Expand Up @@ -95,7 +96,7 @@ export function SentryWallet() {
let operatorWalletCount = 0;

sentryAddressStatusMap.forEach(sentryStatus => {
if(sentryStatus.keyCount > 0){
if (sentryStatus.keyCount > 0) {
operatorWalletCount++;
}
});
Expand All @@ -122,7 +123,7 @@ export function SentryWallet() {

function getOperatorWallets() {
const operatorWallets: string[] = pools.concat(owners);
if(operatorAddress){
if (operatorAddress) {
operatorWallets.push(operatorAddress);
}
return operatorWallets;
Expand Down Expand Up @@ -293,6 +294,9 @@ export function SentryWallet() {
/>

<div className="px-5 pt-4 w-full bg-nulnOil pb-[16px]">

<ClaimBanner />

<InfoBanner
heading="We've revamped how keys are shown on this page"
description="This page now shows all your connected wallets, and their respective keys in a single view. "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import { useOperatorRuntime } from "@/hooks/useOperatorRuntime";
import { RiKey2Line } from "react-icons/ri";
import BaseCallout from "@sentry/ui/src/rebrand/callout/BaseCallout";
import {TextButton} from "@sentry/ui/dist/src/rebrand/buttons/TextButton";
import { getKeyCountFromOperatorData } from "@/utils/getKeyCountFromOperatorData";

export function KeysCard() {
const setDrawerState = useSetAtom(drawerStateAtom);
const {owners, licensesList} = useAtomValue(chainStateAtom);
const {owners, operatorWalletData} = useAtomValue(chainStateAtom);
const {accruing} = useAtomValue(accruingStateAtom);
const keyCount = licensesList.length;
const { sentryRunning } = useOperatorRuntime();

const keyCount = getKeyCountFromOperatorData(operatorWalletData);

return (
<Card width={"341px"} height={"279px"} customClasses="bg-nulnOil shadow-default overflow-visible z-10">
<div className="flex flex-row justify-between items-center py-5 px-6 border-b border-chromaphobicBlack">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ import {MdRefresh} from "react-icons/md";
import { HelpIcon } from "@sentry/ui/src/rebrand/icons/IconsComponents";
import { BiLoaderAlt } from "react-icons/bi";
import BaseCallout from "@sentry/ui/src/rebrand/callout/BaseCallout";
import { getKeyCountFromOperatorData } from "@/utils/getKeyCountFromOperatorData";

export function NetworkRewardsCard() {
const {owners, licensesList} = useAtomValue(chainStateAtom);
const {owners, operatorWalletData} = useAtomValue(chainStateAtom);
const {balances, isBalancesLoading, balancesFetchedLast, accruing, kycRequired} = useAtomValue(accruingStateAtom);
const {combinedOwners} = useCombinedOwners(owners);
const {data: earnedEsxaiBalance} = useGetWalletBalance(combinedOwners);
const [currentTime, setCurrentTime] = useState(new Date());
const {refresh} = useChainDataRefresh();
const keyCount = licensesList.length;

const keyCount = getKeyCountFromOperatorData(operatorWalletData);

const [esXaiBalance, setEsXaiBalance] = useState("--");
const [accruedEsXaiBalance, setAccruedEsXaiBalance] = useState("--");
// const [accruedEsXaiBalance, setAccruedEsXaiBalance] = useState("--");
const [_, setAccruedEsXaiBalance] = useState("--");

// Calculate the time difference in minutes
const calculateTimeDifference = (currentTime: Date, lastUpdateTime: Date) => {
Expand Down Expand Up @@ -88,7 +91,7 @@ export function NetworkRewardsCard() {
</div>

<div className="flex flex-row justify-between items-center gap-1 text-[#A3A3A3]">
{!isBalancesLoading && balancesFetchedLast ? (
{!isBalancesLoading ? (
<div className="flex flex-row justify-center items-center gap-1">
<a onClick={refresh} className="cursor-pointer">
<MdRefresh size={20} color={"#FF0030"}/>
Expand Down Expand Up @@ -133,7 +136,8 @@ export function NetworkRewardsCard() {
</div>
</div>

<div className="px-6 py-3 border-b border-chromaphobicBlack">
{/* TODO this needs to be readded with #188241567 */}
{/* <div className="px-6 py-3 border-b border-chromaphobicBlack">
<div className="flex justify-between items-center text-[#A3A3A3]">
<div className="flex items-center gap-1 text-lg text-elementalGrey">
<h3 className="font-medium">Accrued esXAI</h3>
Expand All @@ -156,7 +160,7 @@ export function NetworkRewardsCard() {
</p>
</div>
</div>
</div>
</div> */}

<div className="px-6 py-3 border-b border-chromaphobicBlack">
<div className="flex items-center gap-1 text-lg text-elementalGrey">
Expand Down
58 changes: 33 additions & 25 deletions apps/sentry-client-desktop/src/features/keys/HasKeys.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,62 @@
import {AiOutlinePlus} from "react-icons/ai";
import {useState} from "react";
import {BulkOwnerOrPool, config} from "@sentry/core";
import {CustomTooltip, PrimaryButton} from "@sentry/ui";
import {drawerStateAtom, DrawerView} from "@/features/drawer/DrawerManager";
import {useAtomValue, useSetAtom} from "jotai";
import {RemoveWalletModal} from "@/features/home/modals/RemoveWalletModal";
import {WalletAssignedMap} from "@/features/keys/Keys";
import {modalStateAtom, ModalView} from "@/features/modal/ModalManager";
import {useOperator} from "@/features/operator";
import {ethers} from "ethers";
import {useGetWalletBalance} from "@/hooks/useGetWalletBalance";
import {useGetSingleWalletBalance} from "@/hooks/useGetSingleWalletBalance";
import { AiOutlinePlus } from "react-icons/ai";
import { useState } from "react";
import { BulkOwnerOrPool, config } from "@sentry/core";
import {
HelpIcon,
// CustomTooltip,
PrimaryButton
} from "@sentry/ui";
import { drawerStateAtom, DrawerView } from "@/features/drawer/DrawerManager";
import { useAtomValue, useSetAtom } from "jotai";
import { RemoveWalletModal } from "@/features/home/modals/RemoveWalletModal";
import { WalletAssignedMap } from "@/features/keys/Keys";
import { modalStateAtom, ModalView } from "@/features/modal/ModalManager";
import { useOperator } from "@/features/operator";
// import {ethers} from "ethers";
// import {useGetWalletBalance} from "@/hooks/useGetWalletBalance";
// import {useGetSingleWalletBalance} from "@/hooks/useGetSingleWalletBalance";
import {
// HelpIcon,
KeyIcon,
} from "@sentry/ui/src/rebrand/icons/IconsComponents";
import { InfoBanner } from "@/components/InfoBanner";
import { KeysTableBody } from "./KeysTableBody";
import { chainStateAtom } from "@/hooks/useChainDataWithCallback";
import { ClaimBanner } from "@/components/ClaimBanner";

interface HasKeysProps {
combinedOwners: string[],
isWalletAssignedMap: WalletAssignedMap,
operatorWalletData: BulkOwnerOrPool[]
}

export function HasKeys({ combinedOwners, isWalletAssignedMap, operatorWalletData }: HasKeysProps) {
// For #188241567 re add `combinedOwners`
export function HasKeys({ isWalletAssignedMap, operatorWalletData }: HasKeysProps) {
const setDrawerState = useSetAtom(drawerStateAtom);
const setModalState = useSetAtom(modalStateAtom);

const [selectedWallet, setSelectedWallet] = useState<string | null>(null);
const [isRemoveWalletOpen, setIsRemoveWalletOpen] = useState<boolean>(false);
const { isLoading: isOperatorLoading } = useOperator();
const { isLoading: isOperatorLoading, publicKey: operatorAddress } = useOperator();

const { data: earnedEsxaiBalance } = useGetWalletBalance(combinedOwners);
const { data: singleWalletBalance } = useGetSingleWalletBalance(selectedWallet);
const [mouseOverTooltip, setMouseOverTooltip] = useState(false);
// const { data: earnedEsxaiBalance } = useGetWalletBalance(combinedOwners);
// const { data: singleWalletBalance } = useGetSingleWalletBalance(selectedWallet);
// const [mouseOverTooltip, setMouseOverTooltip] = useState(false);

const {
totalKeys
} = useAtomValue(chainStateAtom);

function startAssignment(wallet: string) {
function startAssignment(_: string) {
if (!isOperatorLoading) {
setModalState(ModalView.TransactionInProgress);
window.electron.openExternal(`${config.sentryKeySaleURI}/#/assign-wallet/${wallet}`);
window.electron.openExternal(`${config.sentryKeySaleURI}/#/assign-wallet/${operatorAddress}`);
}
}

function onRemoveWallet(wallet: string) {
setSelectedWallet(wallet);
if(isWalletAssignedMap[wallet]){
setModalState(ModalView.TransactionInProgress)
window.electron.openExternal(`${config.sentryKeySaleURI}/#/unassign-wallet/${wallet}`);
window.electron.openExternal(`${config.sentryKeySaleURI}/#/unassign-wallet/${operatorAddress}`);
return;
}
setIsRemoveWalletOpen(true);
Expand Down Expand Up @@ -86,7 +90,8 @@ export function HasKeys({ combinedOwners, isWalletAssignedMap, operatorWalletDat
</div>
</div>

<div className="flex flex-col px-6">
{/* TODO this needs to be readded with #188241567 */}
{/* <div className="flex flex-col px-6">
<div className="flex items-center gap-1 text-lg text-elementalGrey">
<p>Accrued network rewards</p>
<CustomTooltip
Expand Down Expand Up @@ -126,7 +131,7 @@ export function HasKeys({ combinedOwners, isWalletAssignedMap, operatorWalletDat
</div>

</div>
</div>
</div> */}

{/* TODO this needs to be readded with #188241567 */}
{/* <div className="flex flex-col pl-10">
Expand Down Expand Up @@ -160,6 +165,9 @@ export function HasKeys({ combinedOwners, isWalletAssignedMap, operatorWalletDat

</div>
<div className="px-5 pt-4">

<ClaimBanner />

<InfoBanner
heading="We've revamped how keys are shown on this page"
description="This page now shows all your connected wallets, and their respective keys in a single view. "
Expand Down
Loading
Loading