[xc-admin] add approve/reject/execute/cancel buttons (#576)

* add approve/reject/execute/cancel buttons

* fix useMultisig hook not returning correct isLoading value
This commit is contained in:
Daniel Chew 2023-02-11 06:05:06 +09:00 committed by GitHub
parent 62083eb6d6
commit 0e00fb0d4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 93 additions and 2 deletions

View File

@ -13,6 +13,7 @@ import {
useEffect, useEffect,
useState, useState,
} from 'react' } from 'react'
import toast from 'react-hot-toast'
import { import {
ExecutePostedVaa, ExecutePostedVaa,
getMultisigCluster, getMultisigCluster,
@ -26,6 +27,7 @@ import {
import { ClusterContext } from '../../contexts/ClusterContext' import { ClusterContext } from '../../contexts/ClusterContext'
import { useMultisigContext } from '../../contexts/MultisigContext' import { useMultisigContext } from '../../contexts/MultisigContext'
import CopyIcon from '../../images/icons/copy.inline.svg' import CopyIcon from '../../images/icons/copy.inline.svg'
import { capitalizeFirstLetter } from '../../utils/capitalizeFirstLetter'
import ClusterSwitch from '../ClusterSwitch' import ClusterSwitch from '../ClusterSwitch'
import Loadbar from '../loaders/Loadbar' import Loadbar from '../loaders/Loadbar'
@ -115,6 +117,8 @@ const Proposal = ({
const { cluster } = useContext(ClusterContext) const { cluster } = useContext(ClusterContext)
const { squads, isLoading: isMultisigLoading } = useMultisigContext() const { squads, isLoading: isMultisigLoading } = useMultisigContext()
const proposalStatus = proposal ? Object.keys(proposal.status)[0] : 'unknown'
useEffect(() => { useEffect(() => {
const fetchProposalInstructions = async () => { const fetchProposalInstructions = async () => {
const multisigParser = MultisigParser.fromCluster( const multisigParser = MultisigParser.fromCluster(
@ -145,6 +149,50 @@ const Proposal = ({
fetchProposalInstructions() fetchProposalInstructions()
}, [proposal, squads, cluster]) }, [proposal, squads, cluster])
const handleClickApprove = async () => {
if (proposal && squads) {
try {
await squads.approveTransaction(proposal.publicKey)
toast.success(`Approved proposal ${proposal.publicKey.toBase58()}`)
} catch (e: any) {
toast.error(capitalizeFirstLetter(e.message))
}
}
}
const handleClickReject = async () => {
if (proposal && squads) {
try {
await squads.rejectTransaction(proposal.publicKey)
toast.success(`Rejected proposal ${proposal.publicKey.toBase58()}`)
} catch (e: any) {
toast.error(capitalizeFirstLetter(e.message))
}
}
}
const handleClickExecute = async () => {
if (proposal && squads) {
try {
await squads.executeTransaction(proposal.publicKey)
toast.success(`Executed proposal ${proposal.publicKey.toBase58()}`)
} catch (e: any) {
toast.error(capitalizeFirstLetter(e.message))
}
}
}
const handleClickCancel = async () => {
if (proposal && squads) {
try {
await squads.cancelTransaction(proposal.publicKey)
toast.success(`Cancelled proposal ${proposal.publicKey.toBase58()}`)
} catch (e: any) {
toast.error(capitalizeFirstLetter(e.message))
}
}
}
return proposal !== undefined && return proposal !== undefined &&
multisig !== undefined && multisig !== undefined &&
!isMultisigLoading && !isMultisigLoading &&
@ -153,6 +201,10 @@ const Proposal = ({
<div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4 lg:col-span-2"> <div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4 lg:col-span-2">
<h4 className="h4 font-semibold">Info</h4> <h4 className="h4 font-semibold">Info</h4>
<hr className="border-gray-700" /> <hr className="border-gray-700" />
<div className="flex justify-between">
<div>Status</div>
<div>{Object.keys(proposal.status)[0]}</div>
</div>
<div className="flex justify-between"> <div className="flex justify-between">
<div>Proposal</div> <div>Proposal</div>
<div>{proposal.publicKey.toBase58()}</div> <div>{proposal.publicKey.toBase58()}</div>
@ -169,7 +221,7 @@ const Proposal = ({
<div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4 lg:col-span-1"> <div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4 lg:col-span-1">
<h4 className="h4 mb-4 font-semibold">Results</h4> <h4 className="h4 mb-4 font-semibold">Results</h4>
<hr className="border-gray-700" /> <hr className="border-gray-700" />
<div className="grid grid-cols-3 justify-center gap-4 pt-5 text-center align-middle"> <div className="grid grid-cols-3 justify-center gap-4 text-center align-middle">
<div> <div>
<div className="font-bold">Confirmed</div> <div className="font-bold">Confirmed</div>
<div className="text-lg">{proposal.approved.length}</div> <div className="text-lg">{proposal.approved.length}</div>
@ -185,6 +237,37 @@ const Proposal = ({
</div> </div>
</div> </div>
</div> </div>
{proposalStatus === 'active' ? (
<div className="flex items-center justify-between px-8 pt-3">
<button
className="action-btn text-base"
onClick={handleClickApprove}
>
Approve
</button>
<button
className="sub-action-btn text-base"
onClick={handleClickReject}
>
Reject
</button>
</div>
) : proposalStatus === 'executeReady' ? (
<div className="flex items-center justify-between px-8 pt-3">
<button
className="action-btn text-base"
onClick={handleClickExecute}
>
Execute
</button>
<button
className="sub-action-btn text-base"
onClick={handleClickCancel}
>
Cancel
</button>
</div>
) : null}
</div> </div>
<div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4"> <div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4">
<h4 className="h4 font-semibold">Instructions</h4> <h4 className="h4 font-semibold">Instructions</h4>

View File

@ -95,6 +95,7 @@ export const useMultisig = (wallet: Wallet): MultisigHookData => {
publicKey: new PublicKey(0), publicKey: new PublicKey(0),
}, },
}) })
if (cancelled) return
setUpgradeMultisigAccount( setUpgradeMultisigAccount(
await squads.getMultisig( await squads.getMultisig(
UPGRADE_MULTISIG[getMultisigCluster(cluster)] UPGRADE_MULTISIG[getMultisigCluster(cluster)]
@ -109,6 +110,7 @@ export const useMultisig = (wallet: Wallet): MultisigHookData => {
} else { } else {
setSecurityMultisigAccount(undefined) setSecurityMultisigAccount(undefined)
} }
if (cancelled) return
setUpgradeMultisigProposals( setUpgradeMultisigProposals(
await getSortedProposals( await getSortedProposals(
squads, squads,
@ -144,7 +146,9 @@ export const useMultisig = (wallet: Wallet): MultisigHookData => {
} }
})() })()
return () => {} return () => {
cancelled = true
}
}, [urlsIndex, cluster, wallet]) }, [urlsIndex, cluster, wallet])
return { return {

View File

@ -284,3 +284,7 @@
.action-btn { .action-btn {
@apply h-[45px] rounded-full bg-pythPurple px-8 font-mono font-semibold uppercase leading-none transition-colors hover:bg-mediumSlateBlue disabled:opacity-70 disabled:hover:bg-pythPurple; @apply h-[45px] rounded-full bg-pythPurple px-8 font-mono font-semibold uppercase leading-none transition-colors hover:bg-mediumSlateBlue disabled:opacity-70 disabled:hover:bg-pythPurple;
} }
.sub-action-btn {
@apply h-[45px] rounded-full bg-darkGray2 px-8 font-mono font-semibold uppercase leading-none transition-colors hover:bg-darkGray4 disabled:opacity-70 disabled:hover:bg-darkGray2;
}