Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b184580
feat(web): vesting dashboard prototype
Katteu Jul 5, 2024
0858669
Merge branch 'main' of github.com:Katteu/coinecta-frontend into feat/…
Katteu Jul 9, 2024
a60fab9
fix: vesting dashboard update
Katteu Jul 9, 2024
0529290
fix: modified column headings
Katteu Jul 9, 2024
68fd530
Merge branch 'feat/vesting-page-v1' of github.com:Katteu/coinecta-fro…
christiangantuangco Sep 11, 2024
0422315
feat: fetchClaimEntriesByAddress endpoint initial integration to fron…
christiangantuangco Sep 18, 2024
1021793
fix: update baseURL for vesting API service
Mercurial Sep 19, 2024
4a05747
feat: display vesting positions using the fetchClaimEntriesByAddresse…
christiangantuangco Sep 20, 2024
7f1852b
fix: display proper wallet icon to the vesting positions table
christiangantuangco Sep 20, 2024
549e854
chore: removed unecessary imports
christiangantuangco Sep 20, 2024
1d59239
feat: added id property for the new FetchClaimEntriesByAddresses endp…
christiangantuangco Sep 20, 2024
261bd81
feat: Implement redeem functionality and improve the UI
kenjiebalona Sep 20, 2024
793a165
feat: add redeem confirmation dialog
kenjiebalona Sep 25, 2024
57fec44
feat: display only the multi asset and exclude the ADA coin
christiangantuangco Sep 25, 2024
9465c99
fix: merge conflict
christiangantuangco Sep 25, 2024
9cde286
fix: removed lovelace decimal conversion function
christiangantuangco Sep 25, 2024
d0aacd3
feat: create decimal converter function to handle multi assets decima…
kenjiebalona Sep 25, 2024
c94fce1
chore: removed unecessary comments and changed decimal point display …
christiangantuangco Sep 26, 2024
8865195
chore: update package manager version and clean up unused variables i…
Mercurial Sep 26, 2024
ab900d2
merge branch main to feat/csv-to-merkle-tree
christiangantuangco Oct 16, 2024
9454570
refactor: added support for claiming using staking key
christiangantuangco Oct 16, 2024
8aa991e
feat: integrate vesting backend
Mercurial Oct 16, 2024
e76e796
refactor: claim using staking key #2
Mercurial Oct 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,5 @@
"text-encoding": "^0.7.0",
"typescript": "5.1.6"
},
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1"
"packageManager": "pnpm@9.11.0+sha512.0a203ffaed5a3f63242cd064c8fb5892366c103e328079318f78062f24ea8c9d50bc6a47aa3567cabefd824d170e78fa2745ed1f16b132e16436146b7688f19b"
}
25 changes: 22 additions & 3 deletions src/components/WalletSelectDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import {
Select,
MenuItem,
Expand All @@ -19,7 +19,11 @@ import WarningAmberOutlinedIcon from '@mui/icons-material/WarningAmberOutlined';
import { walletDataByName } from '@lib/walletsList';
import { useCardano } from '@lib/utils/cardano';

const WalletSelectDropdown = () => {
interface IWalletSelectDropdown {
onWalletDropDownUpdate?: (addresses: string[]) => void;
}

const WalletSelectDropdown: FC<IWalletSelectDropdown> = ({ onWalletDropDownUpdate }) => {
const theme = useTheme()
const router = useRouter()
const { isWalletConnected: _isWalletConnected, setSelectedAddresses: _setSelectedAddresses, getSelectedAddresses: _getSelectedAddresses } = useCardano()
Expand All @@ -40,9 +44,17 @@ const WalletSelectDropdown = () => {
const localStorageSelectedAddresses = getSelectedAddresses()
if (localStorageSelectedAddresses.length > 0) {
setSelectedAddress(localStorageSelectedAddresses)

if (onWalletDropDownUpdate !== undefined) {
onWalletDropDownUpdate(localStorageSelectedAddresses)
}
} else {
setSelectedAddress(wallets.map((wallet) => wallet.changeAddress))
setSelectedAddresses(wallets.map((wallet) => wallet.changeAddress))

if (onWalletDropDownUpdate !== undefined) {
onWalletDropDownUpdate(wallets.map((wallet) => wallet.changeAddress))
}
}
setWalletAddresses(wallets.map((wallet) => wallet.changeAddress))
}
Expand All @@ -65,6 +77,12 @@ const WalletSelectDropdown = () => {
router.push('/user/connected-wallets')
};

const handleOnWalletDropDownClose = useCallback(() => {
if (onWalletDropDownUpdate !== undefined) {
onWalletDropDownUpdate(selectedAddresses);
}
}, [selectedAddresses, onWalletDropDownUpdate])

useEffect(() => {
const execute = async () => {
if (wallets) {
Expand All @@ -87,6 +105,7 @@ const WalletSelectDropdown = () => {
variant="filled"
value={selectedAddresses}
onChange={handleChange}
onClose={handleOnWalletDropDownClose}
renderValue={() => selectedAddresses.length === 0 ? 'No wallet selected' : 'Select displayed wallets'}
MenuProps={{
PaperProps: {
Expand Down Expand Up @@ -132,7 +151,7 @@ const WalletSelectDropdown = () => {
<MenuItem key={item} value={item} disabled={!isConnectedByWallets[item]}>
<Avatar variant='square' sx={{ width: '22px', height: '22px' }} src={theme.palette.mode === "dark" ? walletByName(wallets![i].type)?.iconDark : walletByName(wallets![i].type)?.icon} />
<Checkbox checked={selectedAddresses.indexOf(item) > -1} disabled={!isConnectedByWallets[item]} />
<ListItemText primary={getShorterAddress(item, 6)} />
<ListItemText primary={getShorterAddress(item, 6)} />
{!isConnectedByWallets[item] && <>
<Tooltip title='This wallet is not connected'>
<WarningAmberOutlinedIcon fontSize='small' color='error' />
Expand Down
16 changes: 14 additions & 2 deletions src/components/dashboard/DashboardMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
Zoom,
Typography
} from '@mui/material';
import HomeIcon from '@mui/icons-material/Home';
import MenuOpenIcon from '@mui/icons-material/MenuOpen';
import { useRouter } from 'next/router';

Expand Down Expand Up @@ -46,6 +45,19 @@ const links = {
]
}

const linksVesting = {
Other: [
{
name: 'Overview',
link: '/vesting'
},
{
name: 'Vesting Positions',
link: '/vesting/manage-vesting'
}
]
}

const DashboardMenu: FC<IDashboardMenuProps> = ({ children }) => {
const theme = useTheme()
const desktop = useMediaQuery(theme.breakpoints.up('md'))
Expand Down Expand Up @@ -78,7 +90,7 @@ const DashboardMenu: FC<IDashboardMenuProps> = ({ children }) => {

const drawer = (
<div>
{Object.entries(links).map(([category, linkItems], categoryIndex) => (
{Object.entries((router.asPath.startsWith('/staking') ? links : linksVesting)).map(([category, linkItems], categoryIndex) => (
<div key={category + categoryIndex}>
{category !== "Other" && <Typography variant="overline">
{category}
Expand Down
2 changes: 0 additions & 2 deletions src/components/dashboard/pages/UnlockVested.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, { FC, useEffect, useRef, useState } from 'react';
import {
Box,
Button,
Divider,
Typography,
} from '@mui/material';
import Grid from '@mui/system/Unstable_Grid/Grid';
Expand Down
10 changes: 9 additions & 1 deletion src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const Header: FC = () => {
{
name: "Staking",
link: "/staking"
},
{
name: "Vesting",
link: "/vesting"
}
];

Expand All @@ -54,6 +58,10 @@ const Header: FC = () => {
localStorage.setItem('darkToggle', temp);
};

function getBaseUrl(pathname: string) {
return `/${pathname.split('/')[1]}`;
}

const NavigationListItem: React.FC<INavItemProps> = ({ size, fontWeight, page }) => {
return (
<Grid item>
Expand All @@ -69,7 +77,7 @@ const Header: FC = () => {
mt: '0',
borderRadius: '10px',
height: (fontWeight && fontWeight > 500) || (size && size > 20) ? '3px' : '2px',
background: router.pathname === page.link ? theme.palette.primary.main : '',
background: getBaseUrl(router.pathname) === page.link ? theme.palette.primary.main : '',
width: '100%',
},
}}
Expand Down
242 changes: 242 additions & 0 deletions src/components/vesting/VestingConfirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import DataSpread from "@components/DataSpread";
import { useAlert } from "@contexts/AlertContext";
import { trpc } from "@lib/utils/trpc";
import { BrowserWallet } from "@meshsdk/core";
import { useWallet, useWalletList } from "@meshsdk/react";
import CloseIcon from "@mui/icons-material/Close";
import {
Button,
CircularProgress,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
IconButton,
useMediaQuery,
useTheme,
} from "@mui/material";
import React, { FC, useState, useCallback, useEffect } from "react";

type ClaimEntry = {
id: string;
rootHash: string;
ownerPkh: string;
token: string;
total: number | string;
claimable: number | string;
frequency: string;
nextUnlockDate: string;
endDate: string;
remainingPeriods: string;
ownerAddress: string;
walletType: string;
};

interface IVestingConfirmProps {
open: boolean;
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
claimEntry: ClaimEntry | null;
}

const VestingConfirm: FC<IVestingConfirmProps> = ({
open,
setOpen,
claimEntry,
}) => {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
const [isSigning, setIsSigning] = useState<boolean>(false);
const { addAlert } = useAlert();
const { connected } = useWallet();

const finalizeTransaction = trpc.vesting.finalizeTransaction.useMutation();
const createClaimTreasuryDataMutation =
trpc.vesting.createClaimTreasuryData.useMutation();
const claimTreasuryMutation = trpc.vesting.claimTreasury.useMutation();
const submitClaimTreasuryTransaction =
trpc.vesting.submitClaimTreasuryTransaction.useMutation();

const getStakeAddress = useCallback(async (walletType: string) => {
const wallet = await BrowserWallet.enable(walletType);

const addresses = await wallet.getRewardAddresses();

return addresses[0];
}, []);

const getPaymentAddress = useCallback(async (walletType: string) => {
const wallet = await BrowserWallet.enable(walletType);

const addresses = await wallet.getUsedAddresses();

return addresses[0];
}, []);

const getRawUtxos = useCallback(async (walletType: string) => {
const api = await window.cardano[walletType].enable();

const rawUtxos = await api.getUtxos();

if (rawUtxos === undefined) return;
return rawUtxos;
}, []);

const getRawCollateralUtxo = useCallback(async (walletType: string) => {
const api = await window.cardano[walletType].enable();

const collateral = await api.experimental.getCollateral();

if (collateral === undefined) return;
return collateral[0];
}, []);

const signTx = useCallback(async (walletType: string, unsignedTx: string) => {
const api = await window.cardano[walletType].enable();

const signedTx = await api.signTx(unsignedTx, true);

if (signedTx === undefined) return;
return signedTx;
}, []);

const handleClose = () => setOpen(false);

const handleSubmit = async () => {
setIsSigning(true);
try {
if (claimEntry === null) return;
const walletType = claimEntry.walletType;

const rawUtxos = await getRawUtxos(walletType);
const rawCollateralUtxo = await getRawCollateralUtxo(walletType);
if (rawUtxos === undefined || rawCollateralUtxo === undefined) return;

const rootHash: string = claimEntry.rootHash;

const paymentAddress = await getPaymentAddress(walletType);
const stakeAddress = await getStakeAddress(walletType);

if (stakeAddress === undefined) return;

const { updatedRootHash, rawProof, rawClaimEntry } =
await createClaimTreasuryDataMutation.mutateAsync({
rootHash,
ownerAddress: stakeAddress,
});

const id: string = claimEntry.id;

const { unsignedTxRaw, treasuryUtxoRaw } =
await claimTreasuryMutation.mutateAsync({
id,
ownerAddress: paymentAddress,
updatedRootHash,
rawProof,
rawClaimEntry,
rawCollateralUtxo,
rawUtxos,
});

const signedTx = await signTx(walletType, unsignedTxRaw);

if (signedTx === undefined) return;

const { txHash } = await finalizeTransaction.mutateAsync({
unsignedTxCbor: unsignedTxRaw,
txWitnessCbor: signedTx,
});

const ownerPkh = claimEntry.ownerPkh;

const response = await submitClaimTreasuryTransaction.mutateAsync({
id,
ownerPkh,
utxoRaw: treasuryUtxoRaw,
txRaw: txHash,
});
console.log(response);
setOpen(false);
addAlert("success", "Redeemed Treasury");
} catch (ex: any) {
addAlert("error", "Error redeeming treasury");
console.error("Error adding stake", ex);
}
setIsSigning(false);
};

return (
<>
<Dialog
open={open}
onClose={handleClose}
fullScreen={fullScreen}
PaperProps={{
variant: "outlined",
elevation: 0,
}}
sx={{
"& .MuiBackdrop-root": {
backdropFilter: "blur(3px)",
backgroundColor: "rgba(0, 0, 0, 0.5)",
},
}}
>
<DialogTitle
sx={{
textAlign: "center",
fontWeight: "800",
fontSize: "32px",
}}
>
Redeem Treasury
</DialogTitle>
<IconButton
aria-label="close"
onClick={handleClose}
sx={{
position: "absolute",
right: 8,
top: 8,
color: (theme) => theme.palette.grey[500],
}}
>
<CloseIcon />
</IconButton>
<DialogContent
sx={{ minWidth: "350px", maxWidth: !fullScreen ? "460px" : null }}
>
<DataSpread
title={`${claimEntry?.token}`}
data={`${claimEntry?.claimable}`}
/>
</DialogContent>
<DialogActions sx={{ justifyContent: "center", mb: 1 }}>
<Button
variant="contained"
color="secondary"
onClick={handleSubmit}
disabled={!connected}
sx={{ display: isSigning ? "none" : "flex" }}
>
Confirm redeem
</Button>
<Button
variant="outlined"
color="secondary"
onClick={handleClose}
disabled={!connected}
sx={{ display: isSigning ? "none" : "flex" }}
>
Cancel redeem
</Button>
<CircularProgress
sx={{ display: isSigning ? "block" : "none" }}
color="secondary"
/>
</DialogActions>
</Dialog>
</>
);
};

export default VestingConfirm;
Loading