[xc-admin-frontend] XC admin frontend fix part2 (#970)
* Add some missing hook dependencies * Smarter target cluster detection * Show approved/rejected count for active proposals
This commit is contained in:
parent
fbd004f359
commit
813ad34b68
|
@ -24,6 +24,7 @@ import { ParsedAccountPubkeyRow, SignerTag, WritableTag } from './AccountUtils'
|
||||||
import { usePythContext } from '../../contexts/PythContext'
|
import { usePythContext } from '../../contexts/PythContext'
|
||||||
|
|
||||||
import { getMappingCluster, isPubkey } from './utils'
|
import { getMappingCluster, isPubkey } from './utils'
|
||||||
|
import { PythCluster } from '@pythnetwork/client'
|
||||||
|
|
||||||
const GovernanceInstructionView = ({
|
const GovernanceInstructionView = ({
|
||||||
instruction,
|
instruction,
|
||||||
|
@ -48,10 +49,11 @@ const GovernanceInstructionView = ({
|
||||||
}
|
}
|
||||||
export const WormholeInstructionView = ({
|
export const WormholeInstructionView = ({
|
||||||
instruction,
|
instruction,
|
||||||
|
cluster,
|
||||||
}: {
|
}: {
|
||||||
instruction: WormholeMultisigInstruction
|
instruction: WormholeMultisigInstruction
|
||||||
|
cluster: PythCluster
|
||||||
}) => {
|
}) => {
|
||||||
const { cluster } = useContext(ClusterContext)
|
|
||||||
const {
|
const {
|
||||||
priceAccountKeyToSymbolMapping,
|
priceAccountKeyToSymbolMapping,
|
||||||
productAccountKeyToSymbolMapping,
|
productAccountKeyToSymbolMapping,
|
||||||
|
|
|
@ -51,6 +51,7 @@ import {
|
||||||
} from '../InstructionViews/AccountUtils'
|
} from '../InstructionViews/AccountUtils'
|
||||||
|
|
||||||
import { getMappingCluster, isPubkey } from '../InstructionViews/utils'
|
import { getMappingCluster, isPubkey } from '../InstructionViews/utils'
|
||||||
|
import { getPythProgramKeyForCluster, PythCluster } from '@pythnetwork/client'
|
||||||
const ProposalRow = ({
|
const ProposalRow = ({
|
||||||
proposal,
|
proposal,
|
||||||
multisig,
|
multisig,
|
||||||
|
@ -94,15 +95,39 @@ const ProposalRow = ({
|
||||||
proposal.publicKey.toBase58().slice(-6)}
|
proposal.publicKey.toBase58().slice(-6)}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="flex space-x-2">
|
||||||
<StatusTag proposalStatus={status} />
|
{proposal.approved.length > 0 && status === 'active' && (
|
||||||
|
<div>
|
||||||
|
<StatusTag
|
||||||
|
proposalStatus="executed"
|
||||||
|
text={`Approved: ${proposal.approved.length}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{proposal.rejected.length > 0 && status === 'active' && (
|
||||||
|
<div>
|
||||||
|
<StatusTag
|
||||||
|
proposalStatus="rejected"
|
||||||
|
text={`Rejected: ${proposal.rejected.length}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<StatusTag proposalStatus={status} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const StatusTag = ({ proposalStatus }: { proposalStatus: string }) => {
|
const StatusTag = ({
|
||||||
|
proposalStatus,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
proposalStatus: string
|
||||||
|
text?: string
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`flex items-center justify-center rounded-full ${
|
className={`flex items-center justify-center rounded-full ${
|
||||||
|
@ -119,7 +144,7 @@ const StatusTag = ({ proposalStatus }: { proposalStatus: string }) => {
|
||||||
: 'bg-pythPurple'
|
: 'bg-pythPurple'
|
||||||
} py-1 px-2 text-xs`}
|
} py-1 px-2 text-xs`}
|
||||||
>
|
>
|
||||||
{proposalStatus}
|
{text || proposalStatus}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -230,7 +255,40 @@ const Proposal = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [instructions, setInstructions] = useState<MultisigInstruction[]>([])
|
const [instructions, setInstructions] = useState<MultisigInstruction[]>([])
|
||||||
const [isTransactionLoading, setIsTransactionLoading] = useState(false)
|
const [isTransactionLoading, setIsTransactionLoading] = useState(false)
|
||||||
const { cluster } = useContext(ClusterContext)
|
const { cluster: contextCluster } = useContext(ClusterContext)
|
||||||
|
const multisigCluster = getMultisigCluster(contextCluster)
|
||||||
|
const targetClusters: (PythCluster | 'unknown')[] = []
|
||||||
|
instructions.map((ix) => {
|
||||||
|
if (ix instanceof PythMultisigInstruction) {
|
||||||
|
targetClusters.push(multisigCluster)
|
||||||
|
} else if (
|
||||||
|
ix instanceof WormholeMultisigInstruction &&
|
||||||
|
ix.governanceAction instanceof ExecutePostedVaa
|
||||||
|
) {
|
||||||
|
ix.governanceAction.instructions.map((ix) => {
|
||||||
|
const remoteClusters: PythCluster[] = [
|
||||||
|
'pythnet',
|
||||||
|
'pythtest-conformance',
|
||||||
|
'pythtest-crosschain',
|
||||||
|
]
|
||||||
|
for (const remoteCluster of remoteClusters) {
|
||||||
|
if (
|
||||||
|
multisigCluster === getMultisigCluster(remoteCluster) &&
|
||||||
|
ix.programId.equals(getPythProgramKeyForCluster(remoteCluster))
|
||||||
|
) {
|
||||||
|
targetClusters.push(remoteCluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
targetClusters.push('unknown')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const uniqueTargetCluster = new Set(targetClusters).size === 1
|
||||||
|
const cluster =
|
||||||
|
uniqueTargetCluster && targetClusters[0] !== 'unknown'
|
||||||
|
? targetClusters[0]
|
||||||
|
: contextCluster
|
||||||
|
|
||||||
const {
|
const {
|
||||||
voteSquads,
|
voteSquads,
|
||||||
|
@ -243,6 +301,7 @@ const Proposal = ({
|
||||||
productAccountKeyToSymbolMapping,
|
productAccountKeyToSymbolMapping,
|
||||||
publisherKeyToNameMapping,
|
publisherKeyToNameMapping,
|
||||||
} = usePythContext()
|
} = usePythContext()
|
||||||
|
|
||||||
const publisherKeyToNameMappingCluster =
|
const publisherKeyToNameMappingCluster =
|
||||||
publisherKeyToNameMapping[getMappingCluster(cluster)]
|
publisherKeyToNameMapping[getMappingCluster(cluster)]
|
||||||
const { publicKey: signerPublicKey } = useWallet()
|
const { publicKey: signerPublicKey } = useWallet()
|
||||||
|
@ -367,6 +426,17 @@ const Proposal = ({
|
||||||
multisig !== undefined &&
|
multisig !== undefined &&
|
||||||
!isMultisigLoading ? (
|
!isMultisigLoading ? (
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
|
<div className="col-span-3 my-2 space-y-4 bg-[#1E1B2F] p-4">
|
||||||
|
<h4 className="h4 font-semibold">
|
||||||
|
{uniqueTargetCluster
|
||||||
|
? `Target Pyth Program: ${targetClusters[0]}`
|
||||||
|
: targetClusters.length == 0
|
||||||
|
? 'No target Pyth program detected'
|
||||||
|
: `Multiple target Pyth programs detected ${targetClusters.join(
|
||||||
|
' '
|
||||||
|
)}`}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
<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">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<h4 className="h4 font-semibold">Info</h4>
|
<h4 className="h4 font-semibold">Info</h4>
|
||||||
|
@ -689,7 +759,10 @@ const Proposal = ({
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{instruction instanceof WormholeMultisigInstruction && (
|
{instruction instanceof WormholeMultisigInstruction && (
|
||||||
<WormholeInstructionView instruction={instruction} />
|
<WormholeInstructionView
|
||||||
|
cluster={cluster}
|
||||||
|
instruction={instruction}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{index !== instructions.length - 1 ? (
|
{index !== instructions.length - 1 ? (
|
||||||
|
|
|
@ -86,6 +86,8 @@ export const PythContextProvider: React.FC<PythContextProviderProps> = ({
|
||||||
connection,
|
connection,
|
||||||
publisherKeyToNameMapping,
|
publisherKeyToNameMapping,
|
||||||
multisigSignerKeyToNameMapping,
|
multisigSignerKeyToNameMapping,
|
||||||
|
priceAccountKeyToSymbolMapping,
|
||||||
|
productAccountKeyToSymbolMapping,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue