Refactor MultisigInstruction (#1098)

* Refactor unknown

* Cleanup

* Cleanup further
This commit is contained in:
guibescos 2023-10-13 16:12:13 +02:00 committed by GitHub
parent 6cebfef1df
commit 5c0c252cee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 165 additions and 298 deletions

View File

@ -15,6 +15,7 @@ import { WormholeMultisigInstruction } from "./WormholeMultisigInstruction";
import { SystemProgramMultisigInstruction } from "./SystemProgramInstruction";
import { BpfUpgradableLoaderInstruction } from "./BpfUpgradableLoaderMultisigInstruction";
import { BPF_UPGRADABLE_LOADER } from "../bpf_upgradable_loader";
import { AnchorAccounts } from "./anchor";
export const UNRECOGNIZED_INSTRUCTION = "unrecognizedInstruction";
export enum MultisigInstructionProgram {
@ -26,22 +27,54 @@ export enum MultisigInstructionProgram {
UnrecognizedProgram,
}
export function getProgramName(program: MultisigInstructionProgram) {
switch (program) {
case MultisigInstructionProgram.PythOracle:
return "Pyth Oracle";
case MultisigInstructionProgram.WormholeBridge:
return "Wormhole";
case MultisigInstructionProgram.MessageBuffer:
return "Message Buffer";
case MultisigInstructionProgram.SystemProgram:
return "System Program";
case MultisigInstructionProgram.BpfUpgradableLoader:
return "BPF Upgradable Loader";
case MultisigInstructionProgram.UnrecognizedProgram:
return "Unknown";
}
}
export interface MultisigInstruction {
readonly program: MultisigInstructionProgram;
readonly name: string;
readonly args: { [key: string]: any };
readonly accounts: AnchorAccounts;
}
export class UnrecognizedProgram implements MultisigInstruction {
readonly program = MultisigInstructionProgram.UnrecognizedProgram;
readonly instruction: TransactionInstruction;
readonly name: string;
readonly args: { [key: string]: any };
readonly accounts: AnchorAccounts;
constructor(instruction: TransactionInstruction) {
this.instruction = instruction;
constructor(
name: string,
args: { [key: string]: any },
accounts: AnchorAccounts
) {
this.name = name;
this.args = args;
this.accounts = accounts;
}
static fromTransactionInstruction(
instruction: TransactionInstruction
): UnrecognizedProgram {
return new UnrecognizedProgram(instruction);
return new UnrecognizedProgram(
UNRECOGNIZED_INSTRUCTION,
{ data: instruction.data },
{ named: {}, remaining: instruction.keys }
);
}
}
export class MultisigParser {

View File

@ -1,22 +1,18 @@
import {
AptosAuthorizeUpgradeContract,
AuthorizeGovernanceDataSourceTransfer,
BpfUpgradableLoaderInstruction,
CosmosUpgradeContract,
EvmSetWormholeAddress,
EvmUpgradeContract,
ExecutePostedVaa,
MessageBufferMultisigInstruction,
MultisigParser,
PythGovernanceAction,
PythMultisigInstruction,
RequestGovernanceDataSourceTransfer,
SetDataSources,
SetFee,
SetValidPeriod,
SystemProgramMultisigInstruction,
UnrecognizedProgram,
WormholeMultisigInstruction,
getProgramName,
} from 'xc_admin_common'
import { AccountMeta, PublicKey } from '@solana/web3.js'
import CopyPubkey from '../common/CopyPubkey'
@ -85,133 +81,96 @@ export const WormholeInstructionView = ({
<>
<div key={`${index}_program`} className="flex justify-between">
<div>Program</div>
<div>
{parsedInstruction instanceof PythMultisigInstruction
? 'Pyth Oracle'
: parsedInstruction instanceof WormholeMultisigInstruction
? 'Wormhole'
: parsedInstruction instanceof
MessageBufferMultisigInstruction
? 'Message Buffer'
: parsedInstruction instanceof
SystemProgramMultisigInstruction
? 'System Program'
: parsedInstruction instanceof
BpfUpgradableLoaderInstruction
? 'BPF Upgradable Loader'
: 'Unknown'}
</div>
<div>{getProgramName(parsedInstruction.program)}</div>
</div>
<div
key={`${index}_instructionName`}
className="flex justify-between"
>
<div>Instruction Name</div>
<div>
{parsedInstruction instanceof PythMultisigInstruction ||
parsedInstruction instanceof WormholeMultisigInstruction ||
parsedInstruction instanceof
MessageBufferMultisigInstruction ||
parsedInstruction instanceof
SystemProgramMultisigInstruction ||
parsedInstruction instanceof BpfUpgradableLoaderInstruction
? parsedInstruction.name
: 'Unknown'}
</div>
<div>{parsedInstruction.name}</div>
</div>
<div
key={`${index}_arguments`}
className="grid grid-cols-4 justify-between"
>
<div>Arguments</div>
{parsedInstruction instanceof PythMultisigInstruction ||
parsedInstruction instanceof WormholeMultisigInstruction ||
parsedInstruction instanceof MessageBufferMultisigInstruction ||
parsedInstruction instanceof SystemProgramMultisigInstruction ||
parsedInstruction instanceof BpfUpgradableLoaderInstruction ? (
Object.keys(parsedInstruction.args).length > 0 ? (
<div className="col-span-4 mt-2 bg-[#444157] p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key</div>
<div>Value</div>
</div>
{Object.keys(parsedInstruction.args).map((key, index) => (
<>
<div
key={index}
className="flex justify-between border-t border-beige-300 py-3"
>
{key === 'lamports' &&
typeof parsedInstruction.args[key] === 'bigint' ? (
<>
<div>{'◎'}</div>
<div>
{lamportsToSol(parsedInstruction.args[key])}
</div>
</>
) : (
<>
<div>{key}</div>
{parsedInstruction.args[key] instanceof
PublicKey ? (
<CopyPubkey
pubkey={parsedInstruction.args[
key
].toBase58()}
/>
) : typeof instruction.args[key] === 'string' &&
isPubkey(instruction.args[key]) ? (
<CopyPubkey
pubkey={parsedInstruction.args[key]}
/>
) : (
<div className="max-w-sm break-all">
{typeof parsedInstruction.args[key] ===
'string'
? parsedInstruction.args[key]
: parsedInstruction.args[key] instanceof
Uint8Array
? parsedInstruction.args[key].toString(
'hex'
)
: typeof parsedInstruction.args[key] ===
'bigint'
? parsedInstruction.args[key].toString()
: JSON.stringify(
parsedInstruction.args[key]
)}
</div>
)}
</>
)}
</div>
{key === 'pub' &&
parsedInstruction.args[key].toBase58() in
publisherKeyToNameMappingCluster ? (
<ParsedAccountPubkeyRow
key={`${index}_${parsedInstruction.args[
key
].toBase58()}`}
mapping={publisherKeyToNameMappingCluster}
title="publisher"
pubkey={parsedInstruction.args[key].toBase58()}
/>
) : null}
</>
))}
{Object.keys(parsedInstruction.args).length > 0 ? (
<div className="col-span-4 mt-2 bg-[#444157] p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key</div>
<div>Value</div>
</div>
) : (
<div className="col-span-3 text-right">No arguments</div>
)
{Object.keys(parsedInstruction.args).map((key, index) => (
<>
<div
key={index}
className="flex justify-between border-t border-beige-300 py-3"
>
{key === 'lamports' &&
typeof parsedInstruction.args[key] === 'bigint' ? (
<>
<div>{'◎'}</div>
<div>
{lamportsToSol(parsedInstruction.args[key])}
</div>
</>
) : (
<>
<div>{key}</div>
{parsedInstruction.args[key] instanceof
PublicKey ? (
<CopyPubkey
pubkey={parsedInstruction.args[
key
].toBase58()}
/>
) : typeof instruction.args[key] === 'string' &&
isPubkey(instruction.args[key]) ? (
<CopyPubkey
pubkey={parsedInstruction.args[key]}
/>
) : (
<div className="max-w-sm break-all">
{typeof parsedInstruction.args[key] ===
'string'
? parsedInstruction.args[key]
: parsedInstruction.args[key] instanceof
Uint8Array
? parsedInstruction.args[key].toString(
'hex'
)
: typeof parsedInstruction.args[key] ===
'bigint'
? parsedInstruction.args[key].toString()
: JSON.stringify(
parsedInstruction.args[key]
)}
</div>
)}
</>
)}
</div>
{key === 'pub' &&
parsedInstruction.args[key].toBase58() in
publisherKeyToNameMappingCluster ? (
<ParsedAccountPubkeyRow
key={`${index}_${parsedInstruction.args[
key
].toBase58()}`}
mapping={publisherKeyToNameMappingCluster}
title="publisher"
pubkey={parsedInstruction.args[key].toBase58()}
/>
) : null}
</>
))}
</div>
) : (
<div className="col-span-3 text-right">Unknown</div>
<div className="col-span-3 text-right">No arguments</div>
)}
</div>
{parsedInstruction instanceof PythMultisigInstruction ||
parsedInstruction instanceof WormholeMultisigInstruction ||
parsedInstruction instanceof MessageBufferMultisigInstruction ||
parsedInstruction instanceof SystemProgramMultisigInstruction ||
parsedInstruction instanceof BpfUpgradableLoaderInstruction ? (
{
<div
key={`${index}_accounts`}
className="grid grid-cols-4 justify-between"
@ -311,54 +270,7 @@ export const WormholeInstructionView = ({
<div>No accounts</div>
)}
</div>
) : parsedInstruction instanceof UnrecognizedProgram ? (
<>
<div
key={`${index}_programId`}
className="flex justify-between"
>
<div>Program ID</div>
<div>
{parsedInstruction.instruction.programId.toBase58()}
</div>
</div>
<div key={`${index}_data`} className="flex justify-between">
<div>Data</div>
<div>
{parsedInstruction.instruction.data.length > 0
? parsedInstruction.instruction.data.toString('hex')
: 'No data'}
</div>
</div>
<div
key={`${index}_keys`}
className="grid grid-cols-4 justify-between"
>
<div>Keys</div>
<div className="col-span-4 mt-2 bg-darkGray4 p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key #</div>
<div>Pubkey</div>
</div>
{parsedInstruction.instruction.keys.map((key, index) => (
<>
<div
key={index}
className="flex justify-between border-t border-beige-300 py-3"
>
<div>Key {index + 1}</div>
<div className="flex space-x-2">
{key.isSigner ? <SignerTag /> : null}
{key.isWritable ? <WritableTag /> : null}
<CopyPubkey pubkey={key.pubkey.toBase58()} />
</div>
</div>
</>
))}
</div>
</div>
</>
) : null}
}
</>
)
})}

View File

@ -12,11 +12,9 @@ import {
MultisigParser,
PythMultisigInstruction,
MessageBufferMultisigInstruction,
UnrecognizedProgram,
WormholeMultisigInstruction,
getManyProposalsInstructions,
SystemProgramMultisigInstruction,
BpfUpgradableLoaderInstruction,
getProgramName,
} from 'xc_admin_common'
import { ClusterContext } from '../../contexts/ClusterContext'
import { useMultisigContext } from '../../contexts/MultisigContext'
@ -249,11 +247,7 @@ const Proposal = ({
const multisigCluster = getMultisigCluster(contextCluster)
const targetClusters: (PythCluster | 'unknown')[] = []
instructions.map((ix) => {
if (
ix instanceof PythMultisigInstruction ||
ix instanceof SystemProgramMultisigInstruction ||
ix instanceof BpfUpgradableLoaderInstruction
) {
if (!(ix instanceof WormholeMultisigInstruction)) {
targetClusters.push(multisigCluster)
} else if (
ix instanceof WormholeMultisigInstruction &&
@ -549,22 +543,9 @@ const Proposal = ({
className="flex justify-between"
>
<div>Program</div>
<div>
{instruction instanceof PythMultisigInstruction
? 'Pyth Oracle'
: instruction instanceof WormholeMultisigInstruction
? 'Wormhole'
: instruction instanceof SystemProgramMultisigInstruction
? 'System Program'
: instruction instanceof BpfUpgradableLoaderInstruction
? 'BPF Upgradable Loader'
: 'Unknown'}
</div>
<div>{getProgramName(instruction.program)}</div>
</div>
{instruction instanceof PythMultisigInstruction ||
instruction instanceof WormholeMultisigInstruction ||
instruction instanceof BpfUpgradableLoaderInstruction ||
instruction instanceof SystemProgramMultisigInstruction ? (
{
<div
key={`${index}_instructionName`}
className="flex justify-between"
@ -572,7 +553,7 @@ const Proposal = ({
<div>Instruction Name</div>
<div>{instruction.name}</div>
</div>
) : null}
}
{instruction instanceof WormholeMultisigInstruction &&
instruction.governanceAction ? (
<>
@ -585,69 +566,64 @@ const Proposal = ({
</div>
</>
) : null}
{instruction instanceof WormholeMultisigInstruction ||
instruction instanceof UnrecognizedProgram ? null : (
{instruction instanceof WormholeMultisigInstruction ? null : (
<div
key={`${index}_arguments`}
className="grid grid-cols-4 justify-between"
>
<div>Arguments</div>
{instruction instanceof PythMultisigInstruction ||
instruction instanceof SystemProgramMultisigInstruction ||
instruction instanceof BpfUpgradableLoaderInstruction ? (
Object.keys(instruction.args).length > 0 ? (
<div className="col-span-4 mt-2 bg-darkGray2 p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key</div>
<div>Value</div>
</div>
{Object.keys(instruction.args).map((key, index) => (
<Fragment key={index}>
<div className="flex justify-between border-t border-beige-300 py-3">
<div>{key}</div>
{instruction.args[key] instanceof PublicKey ? (
<CopyPubkey
pubkey={instruction.args[key].toBase58()}
/>
) : typeof instruction.args[key] === 'string' &&
isPubkey(instruction.args[key]) ? (
<CopyPubkey pubkey={instruction.args[key]} />
) : (
<div className="max-w-sm break-all">
{typeof instruction.args[key] === 'string'
? instruction.args[key]
: instruction.args[key] instanceof Uint8Array
? instruction.args[key].toString('hex')
: JSON.stringify(instruction.args[key])}
</div>
)}
</div>
{key === 'pub' &&
instruction.args[key].toBase58() in
publisherKeyToNameMappingCluster ? (
<ParsedAccountPubkeyRow
key={`${index}_${instruction.args[
key
].toBase58()}`}
mapping={publisherKeyToNameMappingCluster}
title="publisher"
{Object.keys(instruction.args).length > 0 ? (
<div className="col-span-4 mt-2 bg-darkGray2 p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key</div>
<div>Value</div>
</div>
{Object.keys(instruction.args).map((key, index) => (
<Fragment key={index}>
<div className="flex justify-between border-t border-beige-300 py-3">
<div>{key}</div>
{instruction.args[key] instanceof PublicKey ? (
<CopyPubkey
pubkey={instruction.args[key].toBase58()}
/>
) : null}
</Fragment>
))}
</div>
) : (
<div className="col-span-3 text-right">No arguments</div>
)
) : typeof instruction.args[key] === 'string' &&
isPubkey(instruction.args[key]) ? (
<CopyPubkey pubkey={instruction.args[key]} />
) : (
<div className="max-w-sm break-all">
{typeof instruction.args[key] === 'string'
? instruction.args[key]
: instruction.args[key] instanceof Uint8Array
? instruction.args[key].toString('hex')
: JSON.stringify(instruction.args[key])}
</div>
)}
</div>
{key === 'pub' &&
instruction.args[key].toBase58() in
publisherKeyToNameMappingCluster ? (
<ParsedAccountPubkeyRow
key={`${index}_${instruction.args[key].toBase58()}`}
mapping={publisherKeyToNameMappingCluster}
title="publisher"
pubkey={instruction.args[key].toBase58()}
/>
) : null}
</Fragment>
))}
</div>
) : (
<div className="col-span-3 text-right">Unknown</div>
<div className="col-span-3 text-right">No arguments</div>
)}
</div>
)}
{instruction instanceof PythMultisigInstruction ||
instruction instanceof SystemProgramMultisigInstruction ||
instruction instanceof BpfUpgradableLoaderInstruction ? (
{instruction instanceof WormholeMultisigInstruction && (
<WormholeInstructionView
cluster={cluster}
instruction={instruction}
/>
)}
{!(instruction instanceof WormholeMultisigInstruction) ? (
<div
key={`${index}_accounts`}
className="grid grid-cols-4 justify-between"
@ -716,61 +692,7 @@ const Proposal = ({
<div>No arguments</div>
)}
</div>
) : instruction instanceof UnrecognizedProgram ? (
<>
<div
key={`${index}_programId`}
className="flex justify-between"
>
<div>Program ID</div>
<CopyPubkey
pubkey={instruction.instruction.programId.toBase58()}
/>
</div>
<div key={`${index}_data`} className="flex justify-between">
<div>Data</div>
<div className="max-w-sm break-all">
{instruction.instruction.data.length > 0
? instruction.instruction.data.toString('hex')
: 'No data'}
</div>
</div>
<div
key={`${index}_keys`}
className="grid grid-cols-4 justify-between"
>
<div>Keys</div>
<div className="col-span-4 mt-2 bg-darkGray2 p-4 lg:col-span-3 lg:mt-0">
<div className="base16 flex justify-between pt-2 pb-6 font-semibold opacity-60">
<div>Key #</div>
<div>Pubkey</div>
</div>
{instruction.instruction.keys.map((key, index) => (
<>
<div
key={index}
className="flex justify-between border-t border-beige-300 py-3"
>
<div>Key {index + 1}</div>
<div className="flex space-x-2">
{key.isSigner ? <SignerTag /> : null}
{key.isWritable ? <WritableTag /> : null}
<CopyPubkey pubkey={key.pubkey.toBase58()} />
</div>
</div>
</>
))}
</div>
</div>
</>
) : null}
{instruction instanceof WormholeMultisigInstruction && (
<WormholeInstructionView
cluster={cluster}
instruction={instruction}
/>
)}
{index !== instructions.length - 1 ? (
<hr className="border-gray-700" />
) : null}