diff --git a/governance/xc_admin/packages/xc_admin_frontend/components/tabs/General.tsx b/governance/xc_admin/packages/xc_admin_frontend/components/tabs/General.tsx index 927e171470..018b18f1cf 100644 --- a/governance/xc_admin/packages/xc_admin_frontend/components/tabs/General.tsx +++ b/governance/xc_admin/packages/xc_admin_frontend/components/tabs/General.tsx @@ -407,6 +407,7 @@ const General = () => { ) toast.success(`Proposal sent! 🚀 Proposal Pubkey: ${proposalPubkey}`) setIsSendProposalButtonLoading(false) + closeModal() } catch (e: any) { toast.error(capitalizeFirstLetter(e.message)) setIsSendProposalButtonLoading(false) diff --git a/governance/xc_admin/packages/xc_admin_frontend/components/tabs/Proposals.tsx b/governance/xc_admin/packages/xc_admin_frontend/components/tabs/Proposals.tsx index 80e0f79277..50e8802541 100644 --- a/governance/xc_admin/packages/xc_admin_frontend/components/tabs/Proposals.tsx +++ b/governance/xc_admin/packages/xc_admin_frontend/components/tabs/Proposals.tsx @@ -13,6 +13,8 @@ import { import toast from 'react-hot-toast' import { ExecutePostedVaa, + getMultisigCluster, + getProposals, getRemoteCluster, MultisigInstruction, MultisigParser, @@ -23,10 +25,12 @@ import { import { ClusterContext } from '../../contexts/ClusterContext' import { useMultisigContext } from '../../contexts/MultisigContext' import { usePythContext } from '../../contexts/PythContext' +import { PRICE_FEED_MULTISIG } from '../../hooks/useMultisig' import VerifiedIcon from '../../images/icons/verified.inline.svg' import { capitalizeFirstLetter } from '../../utils/capitalizeFirstLetter' import ClusterSwitch from '../ClusterSwitch' import CopyPubkey from '../common/CopyPubkey' +import Spinner from '../common/Spinner' import Loadbar from '../loaders/Loadbar' // check if a string is a pubkey @@ -170,15 +174,19 @@ const ParsedAccountPubkeyRow = ({ const Proposal = ({ proposal, + proposalIndex, instructions, verified, multisig, }: { proposal: TransactionAccount | undefined + proposalIndex: number instructions: MultisigInstruction[] verified: boolean multisig: MultisigAccount | undefined }) => { + const [currentProposal, setCurrentProposal] = useState() + const [isTransactionLoading, setIsTransactionLoading] = useState(false) const [ productAccountKeyToSymbolMapping, setProductAccountKeyToSymbolMapping, @@ -186,9 +194,17 @@ const Proposal = ({ const [priceAccountKeyToSymbolMapping, setPriceAccountKeyToSymbolMapping] = useState<{ [key: string]: string }>({}) const { cluster } = useContext(ClusterContext) - const { squads, isLoading: isMultisigLoading } = useMultisigContext() + const { + squads, + isLoading: isMultisigLoading, + setpriceFeedMultisigProposals, + } = useMultisigContext() const { rawConfig, dataIsLoading, connection } = usePythContext() + useEffect(() => { + setCurrentProposal(proposal) + }, [proposal]) + useEffect(() => { if (!dataIsLoading) { const productAccountMapping: { [key: string]: string } = {} @@ -207,12 +223,36 @@ const Proposal = ({ const proposalStatus = proposal ? Object.keys(proposal.status)[0] : 'unknown' + useEffect(() => { + // update the priceFeedMultisigProposals with previous value but replace the current proposal with the updated one at the specific index + if (currentProposal) { + setpriceFeedMultisigProposals((prevProposals: TransactionAccount[]) => { + prevProposals.splice(proposalIndex, 1, currentProposal) + return [...prevProposals] + }) + } + }, [currentProposal, setpriceFeedMultisigProposals, proposalIndex]) + const handleClickApprove = async () => { if (proposal && squads) { try { + setIsTransactionLoading(true) await squads.approveTransaction(proposal.publicKey) + const proposals = await getProposals( + squads, + PRICE_FEED_MULTISIG[getMultisigCluster(cluster)] + ) + setCurrentProposal( + proposals.find( + (proposal) => + proposal.publicKey.toBase58() === + currentProposal?.publicKey.toBase58() + ) + ) toast.success(`Approved proposal ${proposal.publicKey.toBase58()}`) + setIsTransactionLoading(false) } catch (e: any) { + setIsTransactionLoading(false) toast.error(capitalizeFirstLetter(e.message)) } } @@ -221,9 +261,23 @@ const Proposal = ({ const handleClickReject = async () => { if (proposal && squads) { try { + setIsTransactionLoading(true) await squads.rejectTransaction(proposal.publicKey) + const proposals = await getProposals( + squads, + PRICE_FEED_MULTISIG[getMultisigCluster(cluster)] + ) + setCurrentProposal( + proposals.find( + (proposal) => + proposal.publicKey.toBase58() === + currentProposal?.publicKey.toBase58() + ) + ) toast.success(`Rejected proposal ${proposal.publicKey.toBase58()}`) + setIsTransactionLoading(false) } catch (e: any) { + setIsTransactionLoading(false) toast.error(capitalizeFirstLetter(e.message)) } } @@ -232,9 +286,23 @@ const Proposal = ({ const handleClickExecute = async () => { if (proposal && squads) { try { + setIsTransactionLoading(true) await squads.executeTransaction(proposal.publicKey) + const proposals = await getProposals( + squads, + PRICE_FEED_MULTISIG[getMultisigCluster(cluster)] + ) + setCurrentProposal( + proposals.find( + (proposal) => + proposal.publicKey.toBase58() === + currentProposal?.publicKey.toBase58() + ) + ) toast.success(`Executed proposal ${proposal.publicKey.toBase58()}`) + setIsTransactionLoading(false) } catch (e: any) { + setIsTransactionLoading(false) toast.error(capitalizeFirstLetter(e.message)) } } @@ -243,15 +311,29 @@ const Proposal = ({ const handleClickCancel = async () => { if (proposal && squads) { try { + setIsTransactionLoading(true) await squads.cancelTransaction(proposal.publicKey) + const proposals = await getProposals( + squads, + PRICE_FEED_MULTISIG[getMultisigCluster(cluster)] + ) + setCurrentProposal( + proposals.find( + (proposal) => + proposal.publicKey.toBase58() === + currentProposal?.publicKey.toBase58() + ) + ) toast.success(`Cancelled proposal ${proposal.publicKey.toBase58()}`) + setIsTransactionLoading(false) } catch (e: any) { + setIsTransactionLoading(false) toast.error(capitalizeFirstLetter(e.message)) } } } - return proposal !== undefined && + return currentProposal !== undefined && multisig !== undefined && !isMultisigLoading ? (
@@ -267,15 +349,15 @@ const Proposal = ({
Proposal
- +
Creator
- +
Multisig
- +
@@ -284,17 +366,17 @@ const Proposal = ({
Confirmed
-
{proposal.approved.length}
+
{currentProposal.approved.length}
{proposalStatus === 'active' || proposalStatus === 'rejected' ? (
Rejected
-
{proposal.rejected.length}
+
{currentProposal.rejected.length}
) : (
Cancelled
-
{proposal.cancelled.length}
+
{currentProposal.cancelled.length}
)}
@@ -310,13 +392,13 @@ const Proposal = ({ className="action-btn text-base" onClick={handleClickApprove} > - Approve + {isTransactionLoading ? : 'Approve'}
) : proposalStatus === 'executeReady' ? ( @@ -325,17 +407,59 @@ const Proposal = ({ className="action-btn text-base" onClick={handleClickExecute} > - Execute + {isTransactionLoading ? : 'Execute'}
) : null}
+ {currentProposal.approved.length > 0 ? ( +
+

+ Confirmed: {currentProposal.approved.length} +

+
+ {currentProposal.approved.map((pubkey, idx) => ( +
+
Key {idx + 1}
+ +
+ ))} +
+ ) : null} + {currentProposal.rejected.length > 0 ? ( +
+

+ Rejected: {currentProposal.rejected.length} +

+
+ {currentProposal.rejected.map((pubkey, idx) => ( +
+
Key {idx + 1}
+ +
+ ))} +
+ ) : null} + {currentProposal.cancelled.length > 0 ? ( +
+

+ Cancelled: {currentProposal.cancelled.length} +

+
+ {currentProposal.cancelled.map((pubkey, idx) => ( +
+
Key {idx + 1}
+ +
+ ))} +
+ ) : null}

Total Instructions: {instructions.length} @@ -989,6 +1113,7 @@ const Proposals = () => {
({ @@ -27,6 +28,7 @@ const MultisigContext = createContext({ isLoading: true, error: null, squads: undefined, + setpriceFeedMultisigProposals: () => {}, }) export const useMultisigContext = () => useContext(MultisigContext) @@ -48,6 +50,7 @@ export const MultisigContextProvider: React.FC< upgradeMultisigProposals, priceFeedMultisigProposals, allProposalsIxsParsed, + setpriceFeedMultisigProposals, } = useMultisig(anchorWallet as Wallet) const value = useMemo( @@ -57,6 +60,7 @@ export const MultisigContextProvider: React.FC< upgradeMultisigProposals, priceFeedMultisigProposals, allProposalsIxsParsed, + setpriceFeedMultisigProposals, isLoading, error, squads, @@ -70,6 +74,7 @@ export const MultisigContextProvider: React.FC< upgradeMultisigProposals, priceFeedMultisigProposals, allProposalsIxsParsed, + setpriceFeedMultisigProposals, ] ) diff --git a/governance/xc_admin/packages/xc_admin_frontend/hooks/useMultisig.ts b/governance/xc_admin/packages/xc_admin_frontend/hooks/useMultisig.ts index a26d57cd64..2f3ee8db63 100644 --- a/governance/xc_admin/packages/xc_admin_frontend/hooks/useMultisig.ts +++ b/governance/xc_admin/packages/xc_admin_frontend/hooks/useMultisig.ts @@ -46,6 +46,9 @@ interface MultisigHookData { upgradeMultisigProposals: TransactionAccount[] priceFeedMultisigProposals: TransactionAccount[] allProposalsIxsParsed: MultisigInstruction[][] + setpriceFeedMultisigProposals: React.Dispatch< + React.SetStateAction + > } const getSortedProposals = async ( @@ -229,5 +232,6 @@ export const useMultisig = (wallet: Wallet): MultisigHookData => { upgradeMultisigProposals, priceFeedMultisigProposals, allProposalsIxsParsed, + setpriceFeedMultisigProposals, } }