Skip to content
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

add undelegate and redelegate txns in multisig #968

Merged
merged 9 commits into from
Dec 19, 2023
47 changes: 45 additions & 2 deletions frontend/src/app/(routes)/multisig/components/AccountInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useAppSelector } from '@/custom-hooks/StateHooks';
import { useAppDispatch, useAppSelector } from '@/custom-hooks/StateHooks';
import { deleteMultisig } from '@/store/features/multisig/multisigSlice';
import { RootState } from '@/store/store';
import { MultisigAccount } from '@/types/multisig';
import { getLocalDate } from '@/utils/datetime';
Expand All @@ -7,6 +8,7 @@ import { formatCoin, formatStakedAmount, shortenAddress } from '@/utils/util';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
import DialogDeleteMultisig from './DialogDeleteMultisig';

interface AccountInfoProps {
chainID: string;
Expand Down Expand Up @@ -73,6 +75,7 @@ const AccountInfo: React.FC<AccountInfoProps> = (props) => {
</div>
</div>
<AccountDetails
chainName={chainName}
multisigAccount={multisigAccount}
actionsRequired={actionsRequired}
balance={formatCoin(availableBalance, coinDenom)}
Expand All @@ -93,14 +96,46 @@ const AccountDetails = ({
actionsRequired,
balance,
stakedBalance,
chainName,
}: {
multisigAccount: MultisigAccount;
actionsRequired: number;
balance: string;
stakedBalance: string;
chainName: string;
}) => {
const { account: accountInfo, pubkeys } = multisigAccount;
const { address, name, created_at, threshold } = accountInfo;
const [deleteDialogOpen, setDeleteDialogOpen] = useState<boolean>(false);
const dispatch = useAppDispatch();
const deleteMultisigRes = useAppSelector(
(state: RootState) => state.multisig.deleteMultisigRes
)

const router = useRouter();

const handleGoBack = () => {
router.push(`/multisig/${chainName}`);
};

const handleDeleteDialogClose = () => {
setDeleteDialogOpen(false);
};

useEffect(()=>{
if (deleteMultisigRes?.status === 'idle') {
handleDeleteDialogClose();
handleGoBack();
}
}, [deleteMultisigRes?.status])

const handleDelete = () => {
dispatch(deleteMultisig({
data: { address: multisigAccount?.account?.address },
queryParams: { address: '', signature: '' }
}))
}

return (
<div className="rounded-2xl w-full bg-[#0E0B26] h-full">
<div className="multisig-info-title">
Expand Down Expand Up @@ -165,9 +200,17 @@ const AccountDetails = ({
</div>
</div>
<div>
<button className="delete-multisig-btn">Delete Multisig</button>
<button
onClick={() => setDeleteDialogOpen(true)}
className="delete-multisig-btn">Delete Multisig</button>
</div>
</div>

<DialogDeleteMultisig
open={deleteDialogOpen}
onClose={() => handleDeleteDialogClose()}
deleteTx={handleDelete}
/>
</div>
);
};
Expand Down
132 changes: 132 additions & 0 deletions frontend/src/app/(routes)/multisig/components/AllTransactionsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import useGetChainInfo from '@/custom-hooks/useGetChainInfo';
import { MultisigAddressPubkey, Txn, Txns } from '@/types/multisig';
import { EMPTY_TXN } from '@/utils/constants';
import React, { useMemo, useState } from 'react';
import DialogViewRaw from './DialogViewRaw';
import DialogTxnFailed from './DialogTxnFailed';
import DialogViewTxnMessages from './DialogViewTxnMessages';
import TransactionCard from './TransactionCard';
import { isMultisigMember } from '@/utils/util';

interface AllTransactionsListProps {
chainID: string;
txnsState: Txns;
isHistory: boolean;
}

const AllTransactionsList: React.FC<AllTransactionsListProps> = (props) => {
const { chainID, txnsState, isHistory } = props;

const [msgDialogOpen, setMsgDialogOpen] = useState<boolean>(false);
const [viewRawOpen, setViewRawDialogOpen] = useState<boolean>(false);
const [viewErrorOpen, setViewErrorDialogOpen] = useState<boolean>(false);

const toggleMsgDialogOpen = () => {
setMsgDialogOpen((prevState) => !prevState);
};

const toggleViewRawDialogOpen = () => {
setViewRawDialogOpen((prevState) => !prevState);
};

const handleMsgDialogClose = () => {
setMsgDialogOpen(false);
};

const [selectedTxn, setSelectedTxn] = useState<Txn>(EMPTY_TXN);
const [errMsg, setErrMsg] = useState('');
const [pubKeys, setPubKeys] = useState<MultisigAddressPubkey[]>([]);
const [multisigAddress, setMultisigAddress] = useState<string>('');
const [threshold, setThreshold] = useState<number>(0);

const onViewMoreAction = (txn: Txn) => {
const { pubkeys=[], multisig_address="", threshold=0 } = txn;
setSelectedTxn(txn);
setMsgDialogOpen(true);
setPubKeys(pubkeys);
setMultisigAddress(multisig_address);
setThreshold(threshold);
};

const onViewRawAction = (txn: Txn) => {
setSelectedTxn(txn);
setViewRawDialogOpen(true);
};

const onViewError = (errMsg: string) => {
setErrMsg(errMsg);
setViewErrorDialogOpen(true);
};

const { getDenomInfo, getChainInfo } = useGetChainInfo();
const { explorerTxHashEndpoint, address: walletAddress } = getChainInfo(chainID);
const { decimals, displayDenom, minimalDenom } = getDenomInfo(chainID);
const currency = useMemo(
() => ({
coinMinimalDenom: minimalDenom,
coinDecimals: decimals,
coinDenom: displayDenom,
}),
[minimalDenom, decimals, displayDenom]
);

return (
<div className="pb-6 space-y-6 text-[14px] flex flex-col justify-between">
{txnsState.list.map((txn) => {
const mAddress = txn.multisig_address;
const pKeys = txn.pubkeys || [];
const threshold_value = txn.threshold || 0;
const isMember = isMultisigMember(pKeys, walletAddress);

return (
<TransactionCard
key={txn.id}
isMember={isMember}
txn={txn}
multisigAddress={mAddress}
threshold={threshold_value}
membersCount={pKeys.length}
chainID={chainID}
isHistory={isHistory}
onViewMoreAction={onViewMoreAction}
currency={currency}
onViewRawAction={onViewRawAction}
onViewError={onViewError}
explorerTxHashEndpoint={explorerTxHashEndpoint}
/>
);
})}
{!txnsState.list.length ? (
<div className="mt-16 text-[14px] text-center">- No Transactions -</div>
) : null}
<DialogViewTxnMessages
open={msgDialogOpen}
txn={selectedTxn}
multisigAddress={multisigAddress}
threshold={threshold}
pubKeys={pubKeys}
membersCount={pubKeys.length}
chainID={chainID}
isHistory={isHistory}
toggleMsgDialogOpen={toggleMsgDialogOpen}
currency={currency}
onViewRawAction={onViewRawAction}
explorerTxHashEndpoint={explorerTxHashEndpoint}
onViewError={onViewError}
handleMsgDialogClose={handleMsgDialogClose}
/>
<DialogViewRaw
open={viewRawOpen}
onClose={toggleViewRawDialogOpen}
txn={selectedTxn}
/>
<DialogTxnFailed
open={viewErrorOpen}
onClose={() => setViewErrorDialogOpen(false)}
errMsg={errMsg}
/>
</div>
);
};

export default AllTransactionsList;
29 changes: 16 additions & 13 deletions frontend/src/app/(routes)/multisig/components/BroadCastTxn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '@/store/features/multisig/multisigSlice';
import { RootState } from '@/store/store';
import { getWalletAmino } from '@/txns/execute';
import { MultisigAccount, Pubkey, Txn } from '@/types/multisig';
import { MultisigAddressPubkey, Pubkey, Txn } from '@/types/multisig';
import { getAuthToken } from '@/utils/localStorage';
import { NewMultisigThresholdPubkey } from '@/utils/util';
import { SigningStargateClient, makeMultisignedTx } from '@cosmjs/stargate';
Expand All @@ -19,12 +19,15 @@ import { FAILED_TO_BROADCAST_ERROR } from '@/utils/errors';

interface BroadCastTxnProps {
txn: Txn;
multisigAccount: MultisigAccount;
multisigAddress: string;
threshold: number;
pubKeys: MultisigAddressPubkey[];
chainID: string;
isMember: boolean;
}

const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
const { txn, multisigAccount, chainID } = props;
const { txn, multisigAddress, pubKeys, threshold, chainID, isMember } = props;
const dispatch = useAppDispatch();
const [load, setLoad] = useState(false);
const { getChainInfo } = useGetChainInfo();
Expand Down Expand Up @@ -62,7 +65,7 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
const client = await SigningStargateClient.connect(rpc);

const multisigAcc = await client.getAccount(
multisigAccount?.account.address
multisigAddress
);
if (!multisigAcc) {
dispatch(
Expand All @@ -75,10 +78,10 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
return;
}

const mapData = multisigAccount.pubkeys || {};
let pubkeys: Pubkey[] = [];
const mapData = pubKeys || [];
let pubkeys_list: Pubkey[] = [];

pubkeys = mapData.map((p) => {
pubkeys_list = mapData.map((p) => {
const parsed = p?.pubkey;
const obj = {
type: parsed?.type,
Expand All @@ -88,8 +91,8 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
});

const multisigThresholdPK = NewMultisigThresholdPubkey(
pubkeys,
`${multisigAccount?.account?.threshold}`
pubkeys_list,
`${threshold}`
);

const txBody = {
Expand Down Expand Up @@ -125,7 +128,7 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
queryParams: queryParams,
data: {
txId: txn?.id,
address: multisigAccount?.account.address,
address: multisigAddress,
body: {
status: MultisigTxStatus.SUCCESS,
hash: result?.transactionHash || '',
Expand All @@ -146,7 +149,7 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
queryParams: queryParams,
data: {
txId: txn.id,
address: multisigAccount?.account.address,
address: multisigAddress,
body: {
status: MultisigTxStatus.FAILED,
hash: result?.transactionHash || '',
Expand All @@ -171,7 +174,7 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
queryParams: queryParams,
data: {
txId: txn?.id,
address: multisigAccount?.account.address,
address: multisigAddress,
body: {
status: MultisigTxStatus.FAILED,
hash: '',
Expand All @@ -184,7 +187,7 @@ const BroadCastTxn: React.FC<BroadCastTxnProps> = (props) => {
};
return (
<button
className="sign-broadcast-btn justify-center flex"
className={isMember ? 'sign-broadcast-btn' : 'sign-broadcast-btn btn-disabled'}
onClick={() => {
broadcastTxn();
}}
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/app/(routes)/multisig/components/DeleteTxn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ interface DeleteTxnProps {
txId: number;
address: string;
chainID: string;
isMember: boolean;
}

const DeleteTxn: React.FC<DeleteTxnProps> = (props) => {
const { txId, address, chainID } = props;
const { txId, address, chainID, isMember } = props;
const dispatch = useAppDispatch();
const [deleteDialogOpen, setDeleteDialogOpen] = useState<boolean>(false);
const { getChainInfo } = useGetChainInfo();
Expand Down Expand Up @@ -43,6 +44,7 @@ const DeleteTxn: React.FC<DeleteTxnProps> = (props) => {
<>
<button
className="action-image justify-center items-center flex"
disabled={!isMember}
onClick={() => setDeleteDialogOpen(true)}
>
<Image
Expand Down
Loading